通过强大的五步因果影响框架释放你作为商业分析师的全部潜力
因果推断可以帮助你成为一名商业分析师明星
·发表于 Towards Data Science ·阅读时长 7 分钟·2023 年 11 月 22 日
–
在商业环境中,领导层通常对决策或事件对 KPI 的影响感兴趣。 作为一名绩效分析师,我的大部分时间都在回答这样的问题:“{新闻、政府公告、特殊事件…} 对国家 X 的表现有何影响?”。直观地,如果我们知道新闻/公告/特殊事件从未发生过的情况下会发生什么,我们就能回答这个问题。
这就是因果推断的本质,一些非常有才华的人正在努力使因果推断框架对我们可用。
照片由 Andrew George 提供,来源于 Unsplash
Google Causal Impact 库就是其中之一。由 Google 开发,旨在帮助他们做出更好的营销预算决策,这个库可以帮助我们量化任何事件或干预对感兴趣的时间序列的影响。它听起来可能有些吓人,但其实非常直观。
作为商业分析师,我们应该在日常工作中利用这些工具;以下是你可以采取的 5 个简单步骤,以实施你的第一次因果影响分析。
步骤 1:安装和导入包
本指南将使用 Python。
我们将从安装 Google Causal Impact 包开始。
>pip install tfcausalimpact
你可以在 GitHub 上找到更多关于这个包的信息:github.com/WillianFuks/tfcausalimpact
运行因果影响分析时,你只需要 4 个包。
from causalimpact import CausalImpact
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
第 2 步:导入数据并定义前/后期
我们可以把因果影响框架看作是一个时间序列问题。
在特定日期,我们观察一个事件、新闻等,并跟踪我们的关注指标在该事件后相对于某些基准的变化。你可以将基准视为控制组。
要进行因果影响分析,我们需要以下内容:
-
研究事件的日期
-
我们受影响单位的关注变量的时间序列
-
我们受影响单位的关注变量的时间序列
-
未受事件影响的其他多个单位的关注变量的时间序列
对于我的示例,我使用了以下虚构场景:
-
事件: 2022 年 7 月 9 日在巴塞罗那举行的全球会议
-
关注变量:航空公司的乘客收入
-
受影响单位: 巴塞罗那
-
控制组: 其他欧洲市场
-
问题: 会议对巴塞罗那的乘客收入产生了什么影响?
在考虑时间范围时,应该尽量缩短后期的时间,而前期应该比后期长。
请注意,数据是人为生成的,仅用于说明。
#Define the dates
training_strat = "2022-03-10"
training_end = "2022-06-30"
treatment_start = "2022-07-01"
treatment_end = "2022-07-13"
#import the data- for my example i am using a CSV
data=pd.read_csv("example.csv", index_col='Date', parse_dates=True)
数据框的头部数据
第 3 步:创建控制组
此步骤是最关键的部分。
要估计任何事件或处理的影响,我们需要知道我们的关注单位*(例如:巴塞罗那乘客收入)*在处理和未处理情况下的情况。这是因果推断的主要问题。
我们永远无法在两种互斥的情况下观察我们的关注单位。解决方案是创建一个反事实场景。
你可以把反事实看作是一个假设场景,在这个场景中事件/处理并未发生。
我们的控制组将帮助我们创建这个场景。 要决定将哪些市场包括在控制组中,我们需要计算哪些市场具有预测我们关注市场(巴塞罗那)的预测能力。我们将使用相关性来建立这种预测能力。
此步骤仅需使用前期数据完成。
# get the training data only
df_training = data[data.index <= pd.to_datetime(training_end)]
在使用时间序列检查相关性时,我们需要数据是平稳的,意味着没有趋势或季节性成分,以避免发现虚假的相关性。
# Check for stationarity
from statsmodels.tsa.stattools import adfuller
test = adfuller(x = df_training['Barcelona'])[1]
print(test)
if test< 0.05:
print("Data is stationary")
else:
print("Time series is not stationary")
来自 adfuller 测试的结果
然而,大多数时间序列并非平稳,但我们可以使用称为差分的方法使其平稳。
# Differening calculates the difference or Pct Difference from the previous date
differencing = df_training.pct_change().dropna(thresh = 1,axis=1).dropna()
differencing.head()
每一行表示与前一天的百分比差异。
#Rest on the differenced data
test = adfuller(x = differencing['Barcelona'])[1]
print(test)
if test< 0.05:
print("Data is stationary")
else:
print("Time series is not stationary")
现在,我们可以使用这个新的时间序列来建立市场之间的相关性。
#Create correlation
market_cor= pd.Series(differencing.corr().abs()['Barcelona'])
#Create a list with markets with correlation coef >=0.3
markets_to_keep = list(market_cor[market_cor >=0.3].index)
#Keep only markets on the above list
final_data = data.drop(columns=[col for col in data if col not in markets_to_keep])
我们将用于对照组的市场
步骤 4:实施 CausalImpact
现在,我们准备实施 CausalImpact。
简而言之,CausalImpact 将利用我们的对照组来学习预测巴塞罗那在前期的乘客收入。模型将用此来预测一个没有会议发生的反事实后期情境。
实际发生的情况与反事实情境之间的差异即为会议的影响。
#Prepare Pre and Post periods
pre_period = [training_strat,training_end]
post_period = [treatment_start,treatment_end]
#Fitting CausalImpact
impact = CausalImpact(data=final_data,
pre_period=pre_period,
post_period=post_period)
步骤 5:解释结果与验证
Google CausalImpact 使得结果的可视化和总结变得非常简单。
你可以通过绘制影响图开始。
impact.plot()
print(impact.summary())
Posterior Inference {Causal Impact}
Average Cumulative
Actual 397631.92 5169215.0
Prediction (s.d.) 177248.77 (6914.43) 2304234.0 (89887.6)
95% CI [163527.02, 190631.09] [2125851.11, 2478204.1]
Absolute effect (s.d.) 220383.16 (6914.43) 2864981.0 (89887.6)
95% CI [207000.83, 234104.91] [2691010.9, 3043363.89]
Relative effect (s.d.) 124.34% (3.9%) 124.34% (3.9%)
95% CI [116.79%, 132.08%] [116.79%, 132.08%]
Posterior tail-area probability p: 0.0
Posterior prob. of a causal effect: 100.0%
For more details run the command: print(impact.summary('report'))
根据你测量的内容,你可能对平均影响或累计影响感兴趣。在我们的案例中,我们对会议期间的累计影响感兴趣。
根据分析,会议为巴塞罗那的乘客收入贡献了+280 万美元的增益。
为了更全面的总结,你可以使用以下命令。
print(impact.summary('report'))
Analysis report {CausalImpact}
During the post-intervention period, the response variable had
an average value of approx. 397631.92\. By contrast, in the absence of an
intervention, we would have expected an average response of 177248.77.
The 95% interval of this counterfactual prediction is [163527.02, 190631.09].
Subtracting this prediction from the observed response yields
an estimate of the causal effect the intervention had on the
response variable. This effect is 220383.16 with a 95% interval of
[207000.83, 234104.91]. For a discussion of the significance of this effect,
see below.
Summing up the individual data points during the post-intervention
period (which can only sometimes be meaningfully interpreted), the
response variable had an overall value of 5169215.0.
By contrast, had the intervention not taken place, we would have expected
a sum of 2304234.0\. The 95% interval of this prediction is [2125851.11, 2478204.1].
The above results are given in terms of absolute numbers. In relative
terms, the response variable showed an increase of +124.34%. The 95%
interval of this percentage is [116.79%, 132.08%].
This means that the positive effect observed during the intervention
period is statistically significant and unlikely to be due to random
fluctuations. It should be noted, however, that the question of whether
this increase also bears substantive significance can only be answered
by comparing the absolute effect (220383.16) to the original goal
of the underlying intervention.
The probability of obtaining this effect by chance is very small
(Bayesian one-sided tail-area probability p = 0.0).
This means the causal effect can be considered statistically
significant.
与机器学习不同,Causal Impact 没有准确度测量,这可能使得验证有点棘手。
然而,你可以做 3 件事来验证你的结果。
-
确保你在前期的置信区间不要过于宽泛——这可能表明你的对照组预测能力不足。
-
确保估计影响的置信区间不包含 0。
-
使用反驳测试,例如,如果你进行相同的分析但将事件日期更改为前期的任何一天,影响应为 0。
这是我在工作中最常做的分析之一;一旦你熟悉了这 5 步框架,你也可以使用它,并成为一名商业分析高手。
参考文献
[1]Brodersen, K. H., Gallusser, F., Koehler, J., Remy, N., & Scott, S. L. (2015). 使用贝叶斯结构时间序列模型推断因果影响。
[2]Molak, A., & Jaokar, A. (2023). 《Python 中的因果推断与发现:揭开现代因果机器学习的秘密,涵盖 DoWhy、EconML、PyTorch 等》 [平装本]。5 月 31 日。
[3]research.google/pubs/pub41854/
Kay Brodersen 的《使用 CausalImpact 推断事件影响》
解锁数据访问:在没有 API 端点的情况下利用触发器
使用触发器填补数据拼图中的缺失部分
·发表在 Towards Data Science ·10 分钟阅读·2023 年 6 月 9 日
–
概述
你是否曾经遇到过这样的场景:你尝试通过 API 从一个事务系统(如电子商务系统)中提取一个关键数据点,却发现通过提供的端点无法访问所需的信息?如果是这样,请继续阅读,了解如何有效地利用触发器应对这一挑战。
在没有端点的情况下,我们可能会认为直接从事务表中查询数据是一种选择**。** 直接查询事务表绝对不是一个好主意,因为它可能会对事务系统的性能和稳定性产生重大影响,特别是当涉及到电子商务系统时。当你尝试从实时的电子商务系统中查询数据时,很可能会对用户体验产生不利影响(想象一下在亚马逊购物时需要等待 5 到 10 分钟才能检索到购物车!)。
除此之外,在事务系统的表上运行作业可能会干扰正在进行的事务。如果你考虑在数据仓库表中每天进行“截断-加载”操作,这个问题变得更加重要。此外,上述选项不足以成为可持续的解决方案,因为它不支持平滑的历史数据加载,假设事务系统中定期进行数据清除。
因此,自动化从事务系统中提取数据并将其无缝集成到数据仓库中,同时又不会对系统产生不利影响,就变得至关重要。在这种情况下,数据库触发器提供了一种有效的解决方案。但在我们深入探讨解决方案之前,这里是对触发器的简介。
触发器简介
数据库触发器
数据库触发器是一个经常被忽视的概念,自关系型数据库诞生以来就存在。数据库触发器是一个函数,每当在源表(在此情况下为事务表)中创建、更新(或甚至删除)记录时,都会触发该函数。
数据库触发器分为两种类型:DDL 触发器和 DML 触发器。
DDL 触发器在你希望获取数据库结构更改通知时设置。例如,当你希望在每次定义新模式时得到警报,或者在创建或删除新表时得到通知时,DDL 触发器非常有用。因此,命名为 DDL(数据定义语言)触发器。
DML 触发器在插入、删除或更新新记录时被触发。换句话说,你会在系统中发生数据操作更改时得到通知。一个重要的点是,数据库触发器可以被编程来不仅仅提醒你发生了变化,还可以执行如将数据移动到暂存表等操作。
专用触发器
现代云平台如 Azure 和 AWS 提供了作为其服务一部分的专用触发器。需要注意的是,专用触发器与数据库触发器并不相同。数据库触发器是特定于数据库管理系统(DBMS)的,并且在数据库内部运行,而专用触发器具有更广泛的应用范围。它们可以用于各种自动化任务、事件驱动的工作流,并且能够在云服务及其组件之间创建流畅的集成。
以下是 AWS 作为其云服务的一部分提供的一些专用触发器:
-
AWS Lambda 触发器:这些触发器帮助在指定事件发生时启动一个 lambda 函数。换句话说,你可以指定一个事件来触发 lambda 函数。事件可以是 AWS 内部的,也可以是外部的。内部事件可能与 AWS 服务相关,如 Amazon S3、Amazon DynamoDB 流或 Amazon Kinesis。外部事件可能来自 AWS 外部事务系统的数据库触发器或 IoT 事件。
-
Amazon S3 事件通知:这些触发器使你能够在 S3 存储桶被创建、修改或删除时获得通知。它们使用 AWS 的简单通知服务(SNS)来广播消息。
-
AWS Cloudwatch Events:如果你使用过独立的关系数据库,如 Microsoft SQL Server 和 SQL Server Management Studio (SSMS),你可能使用过 SQL Server Agent 来通知用户作业失败。Cloudwatch 特定于 AWS,不仅用于通知用户作业失败,还用于触发 Lambda 函数和响应事件。CloudWatch Event 和 Lambda Trigger 之间的重要区别在于,虽然 Lambda triggers 指的是 AWS Lambda 响应事件的能力,但 CloudWatch Events 是一个更广泛的事件管理服务,可以处理来自 Lambda 以外来源的事件。顺便提一下,虽然 SQL Server Agent 需要配置邮件服务器,但 Cloudwatch 没有这样的要求。
以下是 Azure 作为其云服务的一部分提供的一些专用触发器:
-
Blob Trigger——Azure blob 类似于 AWS 提供的 S3 buckets。类似于 Amazon S3 通知可以用来获取 S3 buckets 中变化的警报,blob triggers 可以用来获取 Azure blob 容器中变化的通知。
-
Azure Function Trigger——这些是 Azure 相当于 AWS Lambda Function Triggers 的功能。这些触发器可以用来响应 Azure 内部或外部事件来启动 Azure 函数,例如外部事务数据库触发器、HTTP 请求或 IoT 事件中心流。Azure 函数也可以基于预定义的时间表通过 Timer Trigger 启动。
现在我们已经看过了 AWS 和 Azure 提供的不同类型的数据库触发器和专用触发器,让我们回顾一下之前提到的使用案例,以刷新你的记忆。允许我提醒你一下我们之前提到的使用案例。
使用案例——你在事务系统的表中看到了一些你需要用于报告指标的数据点,但这些数据点没有通过你的事务系统的 API 端点提供。因此,你无法使用 Python 或 Java 编写脚本通过 API 获取这些数据点。你也不能在你的事务系统上直接查询,因为这可能会对其性能产生负面影响。
为了解决这个问题,我们使用数据库触发器和云服务提供的专用触发器的组合。以下是一个高级别的方法:
高级别方法(作者提供的图片)
前提条件: 识别你事务系统数据库中那些通过 API 端点无法访问的数据表。一旦识别出这些表,按照以下步骤操作——
步骤 1: 创建一个与事务表具有相同列的暂存表。确保从源事务表中没有任何额外的约束被复制过来。 这样可以尽可能减少对事务系统的影响。此外,还需添加一列来指示所执行的操作,例如插入、更新、删除。假设你的事务表的后端是 SQL Server,以下是需要创建的事务表和暂存表的示例。
-- Sample transactional table
CREATE TABLE Pricing_info (
ProductID INT PRIMARY KEY,
ProductName VARCHAR(50),
Quantity INT,
UnitPrice DECIMAL(10, 2),
OperationDate DATE
);
暂存表将是:
-- Create a Staging table without constraints
CREATE TABLE StagingTable_pricing (
ProductID INT,
ProductName VARCHAR(50),
Quantity INT,
UnitPrice DECIMAL(10, 2),
OperationDate DATE,
OperationType VARCHAR(10)
);
步骤 2: 直接在‘Pricing_info’表(主要事务表)上设置 DML 触发器。
触发器需要被编程,以便每当有新记录插入,或现有记录被更新或删除时,数据将被插入到暂存表中。使用暂存表的目的是避免对主要事务表施加不必要的压力。
以下是相同内容的示例。如下面所示,DML 触发器的两个最重要方面(实际上,任何数据库触发器)是触发事件和触发时间。触发事件指的是应该激活触发器的操作。在这种情况下,我们关注所有 DML 事件,即插入、删除和更新在事务表‘Pricing_info’中。触发时间指的是触发器是在事件发生之前还是之后执行活动。对于我们的用例,显然是‘After’事件触发器。我们创建三个触发器,每个 DML 事件一个。
以下是插入的触发器:
-- Create the trigger
CREATE TRIGGER TransactionTrigger_pricing_Insert
ON Pricing_info
--Trigger Event
AFTER INSERT
AS
BEGIN
-- Insert new records into the staging table
INSERT INTO StagingTable_pricing (ID, Column1, Column2, OperationType)
SELECT ID, Column1, Column2, 'INSERT'
FROM inserted
END;
接下来是更新的触发器:
-- Create the trigger
CREATE TRIGGER TransactionTrigger_pricing_update
ON Pricing_info
--Trigger Event
AFTER UPDATE
AS
BEGIN
-- Insert record in the staging table with the data that was updated
INSERT INTO StagingTable_pricing (ID, Column1, Column2, OperationType)
SELECT ID, Column1, Column2, 'UPDATE'
FROM inserted
END;
最后,我们创建删除的触发器:
-- Create the trigger
CREATE TRIGGER TransactionTrigger_pricing_Delete
ON Pricing_info
--Trigger Event
AFTER DELETE
AS
BEGIN
-- Insert record in the staging table with the data that was deleted
INSERT INTO StagingTable_pricing (ID, Column1, Column2, OperationType)
SELECT ID, Column1, Column2, 'DELETE'
FROM deleted
END;
步骤 3: 现在让我们进入设置专用触发器的部分。
步骤 3a。如果你的数据仓库托管在 AWS 中, 以下是可以实施的高级解决方案。
AWS 实现思路 — 使用 DML 触发器和 AWS Lambda(图片由作者提供)
在上述解决方案中,源是事务系统。我们在事务系统的数据库中设置一个数据库 DML 触发器。每当一个新记录进入事务数据库表时,触发器会将新数据插入到事务数据库中的暂存表中。根据计划(使用 AWS Cloudwatch 事件),Lambda 触发器会触发一个 Lambda 函数,将数据从暂存表抓取到数据仓库(Redshift)中的表中。让我们看看其中的步骤。
前提条件: 在数据仓库中创建一个表以保存事务信息。
步骤 3a.(i)创建一个 AWS Lambda 函数:编写 Lambda 函数的代码,该函数将从暂存表中获取记录,并将其插入到数据仓库表中,同时进行任何必要的计算。
步骤 3b. (ii) 创建 AWS Lambda Trigger — 使用 AWS Cloudwatch 服务安排 Lambda 触发器在夜间时间表中运行 Lambda 函数(建议在业务时间之外或事务系统活动较低的时间段运行 Lambda 函数)。
步骤 3c. (iii) 使用 EventBridge 设置事件映射 — 配置 Lambda 触发器为事件,以便触发器基于指定的事件条件启动。一个典型的场景是按计划触发 Lambda — 每天一次。
AWS 提供了有关设置 Lambda 函数的详细文档,因此不在本文的讨论范围内。
步骤 3b. 如果你的数据仓库托管在 Azure,我们可以使用 Azure Functions 和定时触发器或 Azure 提供的 Azure Function Trigger。
Azure 实现方案 — 使用 DML 触发器和 Azure Functions(作者提供的图片)
在这种情况下,使用定时触发器是一个好主意。当定时触发器激活时,它将运行 Azure Function,然后从暂存表中提取新记录/更新记录/删除记录。(注意:暂存表将具有一个额外的标志变量,指示记录是插入、更新还是删除。)
以下是要遵循的步骤:
步骤 3b. (i):创建 Azure Function:这类似于设置 AWS Lambda 函数。设置代码以从暂存表中提取记录并将其插入数据仓库表中,同时进行任何必要的计算。
步骤 3b. (ii):设置 Azure Function Trigger:使用 Azure Function 应用程序,设置一个定时触发器并指定计划和时间戳参数名称。
步骤 3b. (iii):使用 Azure Eventgrid 设置事件映射:配置触发器将事件数据映射到 Azure Function 中的适当参数。这使得触发器能够基于指定的事件条件启动。
处理历史数据加载
到目前为止,我们讨论的解决方案是针对新数据进入感兴趣的数据点。那么我们如何处理历史数据呢?
为此,一个选项是创建暂存表时执行‘CREATE TABLE AS SELECT’(SQL Server 中的 SELECT * INTO)。这将创建一个预先填充了事务表中当前所有数据的暂存表。其余步骤将保持不变(根据具体情况通过专用的 Azure 定时触发器/AWS Lambda 触发器)。
另一个选项是对事务表中的所有记录执行‘EMPTY UPDATE’。以下是基于当前示例/用例的空更新示例 —
UPDATE TABLE Pricing_info SET OperationDate=OperationDate
如你所见,没有任何值被更新。然而,从数据库的角度来看,所有记录都已更新,因此触发器将为所有更新触发。因此,所有数据都会到达暂存表。请注意,这不是推荐的方法,因为它可能由于生成的更新和撤销语句的数量而拖累交易系统。此外,整个更新操作期间事务表也将被锁定,其他进程无法使用,从而影响交易系统。如果你的事务表非常小,这种方法是可以使用的。
结语
在本文中,我们探索了触发器在捕获通过交易系统的标准 API 端点无法轻易获得的关键数据点方面的多样性。
这些有助于确保数据完整性,并消除对人工干预的需求。因此,它们也确保了关键报告指标的有效纳入。这里提供的解决方案是异步的,以确保交易系统不会受到任何负担。如果你的交易系统没有大量流量(或)没有直接被终端用户应用程序使用,那么它可以设置为同步过程。在这种情况下,lambda 或 Azure 函数需要将触发事件设置为交易数据库的暂存表。同时,还需要提供适当的数据库连接信息。
希望这篇文章对你有帮助。如果你有任何问题,请在评论中告诉我。祝学习愉快!
解锁数据建模成功:3 个必须拥有的上下文表
以及如何免费获取有价值的数据
·
关注 发表在 Towards Data Science ·6 分钟阅读·2023 年 7 月 6 日
–
照片 由 Tobias Fischer 提供,来自 Unsplash
数据建模对于分析团队来说可能是一项具有挑战性的任务。由于每个组织都有独特的业务实体,为每个表找到合适的结构和粒度成为了开放式问题。但不用担心!你需要的一些数据是简单的、免费的,并且占用存储空间极小。
当你的数据完全建模后,你可以看到以下好处:
-
查询生成较为简单,因此更易于阅读。
-
报告更具可扩展性,减少了硬编码的值。
-
你可能花费更少的时间去寻找正确的数据所在位置。
下面是可以简化你团队分析的 3 个通用表,你可以将它们以维度模型的形式导入到你的数据仓库中。
🗓️日期维度
时间序列报告
如果你曾经需要展示某个商业指标在特定时间点的情况,这几乎是一个必备的表。例如,你可能会被问到:
-
“FY23 的销售情况如何?”
-
你能每天展示客户流失情况吗?
管理层常常从时间序列的角度寻求洞察,提出类似“x 的增长或缩小情况如何?”的问题。日期维度可以根据不同的日期属性灵活分析各种指标。
大多数日期维度表可以通过在数据仓库中直接使用DDL语句和日期函数来创建。
在下面的示例中,我使用 BigQuery SQL 来完成这项任务:
CREATE OR REPLACE TABLE `your_project.your_dataset.date_dimension` AS
SELECT
full_date
, EXTRACT(MONTH FROM full_date) AS calendar_month_number
, EXTRACT(YEAR FROM full_date) AS calendar_year
, EXTRACT(QUARTER FROM full_date) AS calendar_quarter
, FORMAT_DATE('%B', full_date) AS calendar_month_name
, EXTRACT(DAYOFWEEK FROM full_date) AS week_name
, FORMAT_DATE('%A', full_date) AS day_name
, CASE
WHEN EXTRACT(DAYOFWEEK FROM full_date) BETWEEN 2 AND 6
THEN TRUE
ELSE FALSE
END AS day_is_weekday
, CASE
WHEN EXTRACT(DAYOFWEEK FROM full_date) = 1 THEN DATE_SUB(full_date, INTERVAL 2 DAY) -- Sunday
WHEN EXTRACT(DAYOFWEEK FROM full_date) = 2 THEN DATE_SUB(full_date, INTERVAL 3 DAY) -- Monday
ELSE DATE_SUB(full_date, INTERVAL 1 DAY)
END AS last_weekday
, EXTRACT(MONTH FROM DATE_ADD(full_date, INTERVAL 6 MONTH)) AS fiscal_month
, EXTRACT(YEAR FROM DATE_ADD(full_date, INTERVAL 6 MONTH)) AS fiscal_year
, EXTRACT(QUARTER FROM DATE_ADD(full_date, INTERVAL 6 MONTH)) AS fiscal_quarter
FROM UNNEST(GENERATE_DATE_ARRAY('2020-01-01', '2050-12-31', INTERVAL 1 DAY)) AS full_date
拆解一下:
-
我们从GENERATE_DATE_ARRAY函数开始,它返回一个你指定范围内的日期数组。然后我们使用 UNNEST 函数将数组的每个元素拆分成单独的行,就像在标准数据库表中一样。BigQuery 中的数组使用一行来显示多个值。
-
然后利用我们从展开数组生成的full_date列(表示日期格式为XXXX-MM-DD),我们可以使用许多 BigQuery 的EXTRACT函数将full_date的各个部分(如月份、日期、年份等)提取到不同的字段中。
-
FORMAT_DATE函数的作用类似于 EXTRACT,但提供了更多自定义日期值显示的选项。你可以使用Google 文档中描述的特殊格式元素来了解每个‘%’字符的意义。
-
我们还使用DATE_SUB函数,它只是从一个日期中减去一个值。这用于获取相关的财政年度,在这个示例中,财政年度将从每个日历年的七月开始。在这个函数中,我们指定一个数量(1 - 无限)和间隔(日、月、年等)。
🌎邮政编码维度
地理空间报告
如果你被要求创建热图可视化或进行一般的地理空间分析,邮政编码维度将对你的团队非常有用。这让你可以通过纬度和经度可视化元素,按县名、时区聚合,并附加人口数据以进行基准比较。
邮政编码维度是客户表的一个很好的补充表。通过将邮政编码字段作为连接键,你可以附加有意义的上下文数据到你的客户基础所在的位置及其背后的模式。
Opendatasoft 提供各种免费的数据集以及开源 API 连接器。适用于此用例的一个数据集是 US Zip Codes Points- United States of America 数据集。在此链接中,移到‘API’标签,配置 URL 以检索 JSON 数据。
几行 Python 代码后,我们可以输出如下 Pandas DataFrame:
import requests
import pandas as pd
url = 'https://data.opendatasoft.com/api/records/1.0/search/?dataset=georef-united-states-of-america-zc-point%40public&q=&facet=stusps_code&facet=ste_name&facet=coty_name&facet=cty_code&facet=zip'
response = requests.get(url)
zips = response.json()
pd.json_normalize(zips,record_path='records')
分解如下:
-
在这里,我使用 requests 库从‘url’变量中显示的 URL 检索数据,该 URL 在 opendatasoft 网站的 API 标签上生成。
-
使用 Pandas,我使用 json_normalize 函数将 JSON 数据转换为 Pandas DataFrame。
📈FX Rates 事实表
用于财务分析
拥有国际客户的组织通常需要将所有交易转换为基础货币以进行财务报告。为了理解外汇汇率波动如何影响收入,每日 FX 汇率数据流是回答这个问题的绝佳解决方案。
当与时间序列报告配合使用时,这也特别具有影响力,可以在特定销售时间附加汇率。在我构建显示客户收入随时间变化的仪表板的经验中,业务用户总是欣赏能够在日期轴上切换不同的外汇汇率值。FX Rates 表格可以让你完成所有这些操作。
Exchangerate.host 是另一个开源网站,允许你连接到每日 FX 汇率数据流。以下是如何检索数据的示例 — 更多信息请参见 他们的文档:
import requests
import pandas as pd
from datetime import date
#Retrieve the latest dates from the exchangerate api
url = 'https://api.exchangerate.host/latest?base=USD'
response = requests.get(url)
rates = response.json()
# Convert JSON list to a Pandas Dataframe & preview
rates_list = list(rates['rates'].items())
df_rates = pd.DataFrame(rates_list, columns=['currency', 'value'])
df_rates['cycle_date'] = date.today()
df_rates.head(10)
分解如下:
-
在这里,我们使用 requests 库从‘url’变量中指定的 URL 检索数据,数据形式为 Python 字典。请注意 — 我根据文档编辑了 URL,以指定基础货币为 USD。即,所有汇率将与其 USD 汇率相关。
-
接下来,我们将 Python 字典转换为列表,从‘rates’键中获取数据。
rates_list = list(rates['rates'].items())
- 然后,我们将列表转换为 Pandas DataFrame 并标记列标题:
df_rates = pd.DataFrame(rates_list, columns=['currency', 'value'])
- 最后,添加了一个名为 ‘cycle_date’ 的列,表示 ETL 周期日期,标示数据何时被摄取到数据仓库中。
结论
将公开可用的数据纳入数据仓库可以为分析团队提供即时价值,且付出最少的努力。这些表格以及任何适当建模的数据实体,消除了仅在像 Power BI 或 Tableau 这样的 BI 工具中存储嵌套业务逻辑的需求。它们提供了一个集中化的数据源,多个分析师可以参考并在报告中一致应用。这种数据建模的整体方法使团队能够轻松扩展报告,确保对源数据的透明度。通过利用这些类型的上下文表格,您的组织可以简化分析流程,消除报告中的不一致,并实现更高水平的数据驱动决策。
祝建模愉快!
解锁决策制定:人工智能桥接理论框架与技术进步
数据科学和人工智能如何帮助决策的简要概述
·发布于 Towards Data Science ·12 min read·2023 年 12 月 11 日
–
图片来源:Jake Melara 于 Unsplash
我们的生活充满了不断的决策和选择。由于我们的决策可能会带来相当大的经济和社会影响,决策研究从一开始就是高度跨学科的。自 20 世纪中叶以来,来自数学、社会学、心理学、经济学、政治学和计算机科学的科学家们一直在积极研究如何做出更好的决策。在这些领域中,最著名的成就包括预期效用理论、前景理论和博弈论。这些理论由数学模型驱动,但通常面临来自实际场景的经验测试的挑战。
随着计算能力的巨大提升和云技术的蓬勃发展,决策支持系统(DSS)与决策理论同步进步,以帮助人类做出决策,特别是在商业和组织环境中。一个典型的 DSS 包括可扩展的知识数据库,用于收集和存储大量信息,统计和分析算法,用于预测和投射,以及用户界面(包括图表和仪表板),供人类决策者可视化和互动决策过程。
然而,大多数人类决策是通过试错的增量学习得出的。迭代方法在处理新环境中的未知因素时特别有效。它需要探索新信息和评估错误以改进决策。值得注意的是,深度强化学习已经模拟了人类决策的试错性质,并在特定游戏中超越了人类玩家。
强化学习(RL)是从一开始就存在的机器学习领域之一。其重大突破发生在深度神经网络应用于模型之后。深度强化学习只是人工智能和深度学习革命化决策领域的一个例子。我们现在正处于一个人工智能为所有决策学科提供基础的时代,加快了对人类决策过程的理解,并进一步赋能人类做出数据驱动的理性决策。
决策过程与决策理论
决策是一个以选择行动方案为结束的过程。简单来说,这个过程有四个连续的阶段:评估、选项、评估和行动选择。每个阶段都依赖于前一个阶段的成功完成。
阶段 1:评估
第一个决策阶段是识别问题并提出决策。为了正确评估问题,我们应该理想地收集所有相关事实,识别当前状态与期望结果之间的差距,并最终确认是否需要做出决定。
问题评估对于理解环境、条件和可能的限制至关重要。收集到的信息越完整准确,接下来的决策阶段就会越顺利,确保做出更好的选择。跳过这个阶段可能会导致完全错误的决定,并带来不利的后果。文献和我们日常生活中的大量故事都教会我们一个教训:人们因为信息不完整和先前的无根据信念而匆忙做出错误决定。
阶段 2:选项
一旦问题得到确认并且所有前提信息已经收集完毕,下一阶段是制定不同的选项,然后做出最终决定。这一步涉及到基于以往知识和经验预测未来的结果。它包括发明、开发和分析决策备选方案,并在可能的情况下测试每个解决方案的可行性。
这个阶段需要技能和专业知识来做出准确的预测。可能需要工具或计算来制定选项。没有这些工具时,人脑使用想象力来基于记忆可视化未来的行动和潜在后果。时间因素也是这个阶段的一个因素:时间越久,准确预测的难度越大。
阶段 3:评估
面对多个不确定的选项时,大多数建立的决策理论都专注于如何评估这些替代方案,以及人类如何做出最终选择,基于每个选项应该有一个预期值的假设。在生物世界中,这个价值可能是减少饥饿、口渴或其他基本生存需求。在许多不同的情况下,这可能是奖励或惩罚。对金融或经济决策来说,最直接的价值是赚取或失去的金钱。
预期效用理论最初由瑞士数学家尼古拉斯·贝尔努利在 1713 年提出,后来由 20 世纪的其他经济学家和数学家完善。该理论提出,理性代理人应该选择具有最大预期效用的选项。预期效用是根据特定情况下某一行动可能达到的所有效用水平的加权平均值。实质上,效用反映了选择的预期值和风险。
让我们用一个简单的例子来看看预期效用理论如何运作。假设有两个情境,每个情境都有两个选项,我们让参与者选择每个情境中的最佳选项:
情境 1:
-
选项 1:100% 几率获得 450 美元
-
选项 2:50% 几率获得 1000 美元
情境 2:
-
选项 1:100% 几率损失 500 美元
-
选项 2:50% 几率损失 1100 美元
通过将概率与预期的美元价值相乘来推导每个选项的效用。根据预期效用理论,参与者应该在每个情境中选择效用最高的选项:情境 1 的第 2 个选项和情境 2 的第 1 个选项。
预期效用理论从经济学角度发展起来,以确保人们做出的选择与他们的目标一致。它还考虑了边际递减,即当金钱的价值增加时,效用的增加程度会减缓(例如,富人对额外的 100 美元的需求低于穷人)。
在 1979 年,心理学家丹尼尔·卡尼曼和阿莫斯·特沃斯基通过实验发现挑战了预期效用理论。他们的研究揭示了人们并不总是选择最大效用,而是根据价值和风险的相对性有倾向地选择更规避风险或寻求风险的选项。这些发现导致了前景理论的发展。
根据上述相同的例子,大多数参与者在情境 1 中选择选项 1,因为 450 美元是 100%确定的。在情境 2 中,大多数人选择了更具风险的选项 2,尽管它的效用值(550 美元)表示比另一个选项损失更多。换句话说,人们更愿意接受较小的效用收益以避免风险,但在面临损失时则愿意承担更多的风险。
因此,与期望效用理论中对收益和损失的对称最大效用计算相比,前景理论的曲线呈不对称的 S 形,负效用曲线明显比正部分陡峭(见下图)。简而言之,期望效用理论讲的是人们应该做什么以做出理性选择以获得最大效用。而前景理论解释了为什么人们往往由于偏好避免立即损失而不是追求长期收益,导致决策变得更差。
前景理论的曲线呈不对称的 S 形,因为人们对立即获得的收益有风险规避,而在面对损失时则会寻求额外的风险。图片来源于作者。
然而在现实生活中,期望值可能会变得模糊,因为它并不是我们人类思维的固有部分。价值可能依赖于幸福感或其他心理或社会因素。此外,在许多情况下,我们尚未确定每个结果的概率。决策理论的一般挑战在于,它们都具有仅适用于特定领域的假设,并且已经通过小样本进行测试。此外,期望值和概率不是静态的,而是依赖于许多因素,并可能相对于其他选项。决策者必须在每次这些因素或选项发生变化时重新评估效用。
第 4 阶段:行动选择
做出最终决定和选择行动方案有时与第 3 阶段不可分割。人类可以分配期望值,找到最大效用,并立即行动。然而,在许多其他情况下,最佳选项在评估后并不明显或不可用。我们常常遇到两种选择在补充或竞争的方式下提供相等的价值,并且选择一个与另一个相比成本似乎相当的情况。典型的困境可能使决策变得困难或使决策过程停滞不前。这时,人们会寻求他人(例如,家人或朋友)的帮助,或利用社会群体来决定。当一个特定问题有多个决策者时,博弈论已经发展起来以解决这种类型的决策困境(注:博弈论是社会决策中的一个重要领域,但超出了当前文章的范围)。
决策支持系统
深思熟虑的决策是人类智慧的一个重要功能。在面对新问题时,人类必须经历上述典型的四个决策阶段。然而,由于大脑容量的限制和潜在的偏见,我们的人脑往往难以合理化复杂的情况。自 20 世纪中叶以来,决策支持系统(DDS)应运而生,以扩展人类能力,帮助决策者做出更好的决策。DDS 的主要特点包括其知识系统和数据驱动的算法,用于进行预测和评估。
知识系统以扩展人类的记忆
我们需要了解世界以解决新问题。对相关事实的准确和完整的把握对于在决策的后续步骤中做出预测和预报至关重要。对于简单的生活问题,依靠我们对经验和知识的回忆可能已经足够。然而,对于更复杂的问题,我们的记忆并不是完全可信的,主要有两个原因:
首先,人类记忆并不是所有信息都能存储。人们只会关注他们注意到的方面,而忽略许多最初被认为不相关的细节。获取的短期记忆如果不被使用会逐渐衰退,只有一小部分被长期保存。有时,即使一个事实已经存储在大脑中,人们也可能在寻找它时遇到困难(例如,搜索问题)。
其次,我们的记忆会不断被修改和更新。我们的脑袋里没有单独的地方来存储我们经历的不同实例。它使用最初记录和处理信息的相同脑区来存储记忆。每当我们回忆或在脑海中重播时,相同的神经网络会被激活,突触的权重会进行调整。此外,由于神经网络的模式识别和联想性质,我们的大脑可以自动填补最初未被记录或观察到的细节。这解释了初始提问和在解决问题过程中隐性暗示如何扭曲人们的记忆以及影响他们的决策。
相反,计算机知识系统和数据库是忠实存储历史事实的工具,不会遗漏或扭曲。它们将原始事实和汇总信息进行有序存储,以便于访问和搜索。随着云计算的进步,各种类型的数据库可以存储不同种类的信息,并具有高扩展性、可靠性和性能。
先进的算法以提供更好的预测
对于深思熟虑的决策,人脑利用想象力来预测未来。尽管这非常强大,但我们的思维确实存在一些限制:
-
人类想象力的预测能力有限,因为它往往会重建并填补当前信息的空白。
-
许多预测需要非线性计算(例如,幂函数),这需要外部工具来帮助完成。
-
人脑使用工作记忆同时保持多个情境,以比较优缺点。工作记忆的容量有限,并且需要高度集中进行强度计算,这在长时间内不可持续。
相反,DDS(决策支持系统)利用数学、统计和机器学习算法,并借助强大的计算资源来帮助人类进行预测和预报。过去几十年中,众多算法和方法被开发、实施并成熟,这极大地帮助人类在各种行业和组织中做出更好的决策。
帮助人类做出理性决策
我们都知道情绪对我们的决策有着显著的影响,尤其是在选项评估过程中。例如,厌恶风险的情绪会导致偏见决策,这在前景理论中有所体现。人类的认知偏差和启发式也会导致主观评估和次优或错误选择。因此,数据分析可以通过利用效用理论及相关效用函数来缓解情绪的影响,克服潜在的偏见,使人们能够做出最佳选择。此外,DSS(决策支持系统)通常具有用户界面来呈现选项和重播情境,使决策过程更具互动性,帮助决策者做出无偏见的数据驱动决策。
未知的未知与强化学习
“…有已知的已知;我们知道我们知道的东西。我们也知道有已知的未知;也就是说,我们知道有些东西我们不知道。但也有未知的未知——那些我们不知道我们不知道的东西。”
– 唐纳德·拉姆斯菲尔德
决策的挑战在于未来的不确定性,尤其是与前美国国防部长唐纳德·拉姆斯菲尔德(2002 年新闻发布会上的名言)所提到的“未知的未知”相关。如果一切都是已知的(已知的已知),那么不再需要决策,因为结果已经确定。“已知的未知”通过选项的概率和风险来表示,这些概率和风险用于效用函数中,以识别具有最大效用的最佳选择。然而,“未知的未知”强调了无法预测或预报的结果的不确定性,它们只能在特定行动发生后被识别或发现。
试错法是处理未知未知问题的解决方案,人类实际上擅长这种方法。他们探索环境,做出最佳选择,并从结果中学习。这个过程不断迭代,直到他们能够做出完美的决策。这就是人类成为任何他们所做事情的高手的方式,包括决策。由于这种迭代学习模式,我们的大多数选择都是自动化的:一些是由自然完全固定的,如反射(例如,从火中撤回手,眨眼以避开强光),另一些是通过训练学习的结果(例如,赢得游戏,驾驶飞机)。
强化学习(RL)作为最古老的机器学习算法之一,使用与人类相同的试错方法进行学习。尽管 RL 最初在 1950 年代设计,但真正的革命发生在 2013 年,当时 Deep Mind 将深度神经网络应用于 RL,使得模型能够从零开始学习游戏,并最终在这些游戏中超越人类。最著名的例子是击败人类世界冠军的 AlphaGo。
深度强化学习模型具有与本文之前概述的类似决策过程。在模型中,代理进行观察、评估最佳路径、采取行动,并通过环境中的奖励获得反馈。随后,代理将其预期奖励与实际奖励进行比较,并利用差异来调整下一轮的行动。通过时间上的迭代调整,它将以最大化奖励的方式行动。值得注意的是,RL 使用“奖励”一词而非“效用”,因为奖励的计算与效用理论中的效用计算有所不同。
深度强化学习(Deep RL)将深度学习应用于典型的 RL 框架。图片来源:维基百科
深度强化学习(Deep RL)相较于决策支持系统(DSS)的优势在于,代理可以通过一系列传感器(如摄像头和触觉感应)自行探索环境和收集信息,并能够采取行动,如发送信号激活电机。换句话说,深度强化学习模仿了人类和动物的大脑,作为一个集成系统涵盖了从头到尾的决策过程,具备持续学习和改进的能力。
到目前为止,许多使用案例成功地利用了深度强化学习,包括游戏、自驾车、推荐系统、网络广告投放和交付、金融市场预测等。此外,人工智能和深度学习为在每个层级上连接碎片化的决策学科开辟了新的途径。以下是一些趋势,但肯定不是详尽无遗的:
-
研究人员使用深度学习算法来评估、测试和推进决策理论,基于大规模实验数据。
-
决策支持系统利用深度强化学习来替代或增强先前的算法,以进行更准确的预测和优化决策。
-
新的决策框架,如动态决策和鲁棒决策,已经出现,它们将传统的一次性繁重决策过程拆分为更小的迭代周期,并利用人工智能从以往的决策中学习并应对不确定性。
-
人工智能和深度神经网络已成为神经科学家研究人类决策机制和寻找预防人类做出如成瘾等错误决策方法的不可或缺的工具。
-
决策理论的进展和新的脑研究发现将帮助人工智能实现更自动化的决策。
结论
在不同的领域中,个人、组织和社会旨在做出与其目标和宗旨一致的最佳决策。鉴于决策在各种背景下的重要性,它已成为多个关键学科的研究对象。五位诺贝尔奖获得者因其在决策及相关的期望效用理论和前景理论上的重大贡献而获得认可。同时,数据科学家通过实施可扩展的决策支持和预测系统,成功地协助了人类决策者。深度强化学习在模拟甚至超越人类决策方面的最新突破标志着一个变革阶段,人工智能在整合决策学科、实现对人类决策过程的统一理解以及将各种决策系统提升到前所未有的自动化水平方面发挥了关键作用。
使用 Airflow 解锁 MLOps:ML 系统编排的全面指南
完整的 7 步 MLOps 框架
第四部分:私人 PyPi 服务器。用 Airflow 编排一切。
·发布于 Towards Data Science ·17 分钟阅读·2023 年 5 月 23 日
–
图片由 Hassan Pasha 提供,来源于 Unsplash
本教程代表7 节课程中的第四部分课,将一步一步指导你如何设计、实现和部署一个 ML 系统,并运用MLOps 好实践。在课程中,你将构建一个准备投入生产的模型,以预测丹麦多个消费者类型在接下来的 24 小时内的能源消耗水平。
在本课程结束时,你将理解如何使用批量服务架构设计、编码和部署 ML 系统的所有基础知识。
本课程针对中级/高级机器学习工程师,希望通过构建自己的端到端项目来提升技能。
如今,证书到处都是。构建可以展示的高级端到端项目是获得专业工程师认可的最佳方式。
目录:
-
课程介绍
-
课程内容
-
数据源
-
第四部分:私人 PyPi 服务器。用 Airflow 编排一切。
-
第四部分:代码
-
结论
-
参考资料
课程介绍
在这个 7 节课程结束时,你将知道如何:
-
设计批量服务架构
-
使用 Hopsworks 作为特征存储
-
设计一个从 API 读取数据的特征工程管道
-
构建一个包含超参数调整的训练管道
-
使用 W&B 作为 ML 平台跟踪你的实验、模型和元数据
-
实现一个批量预测管道
-
使用 Poetry 构建你自己的 Python 包
-
部署你自己的私人 PyPi 服务器
-
使用 Airflow 协调一切
-
使用预测来编写一个使用 FastAPI 和 Streamlit 的 web 应用程序
-
使用 Docker 对代码进行容器化
-
使用 Great Expectations 确保数据的验证和完整性
-
监控预测性能的变化情况
-
将所有内容部署到 GCP
-
使用 GitHub Actions 构建 CI/CD 管道
如果这听起来很多,不要担心。在你完成这门课程后,你将理解我之前说的所有内容。最重要的是,你将知道我为什么使用这些工具,以及它们如何作为一个系统协同工作。
如果你想从这门课程中获得最大的收益, 我建议你访问包含所有课程代码的 GitHub 仓库 。这门课程旨在让你快速阅读和复制文章中的代码。
到课程结束时,你将知道如何实现下图所示的内容。如果有什么不明白的地方,请不要担心。我会详细解释一切。
课程中你将构建的架构图 [作者提供的图片]。
到第四部分课结束时,你将知道如何托管你的 PyPi 仓库,并使用 Airflow 协调三个管道。你将学习如何调度管道以创建每小时的预测。
课程内容:
-
私有 PyPi 服务器。使用 Airflow 协调一切。
-
使用 GE 进行数据验证以确保质量和完整性。模型性能持续监控。
如果你想全面掌握这节课,我们建议你查看课程 1,课程 2 和课程 3,这些课程详细解释了你将在本文中协调的管道实现:
-
特征工程管道
-
训练管道
-
批量预测管道
数据源
我们使用了一个免费且开放的 API,该 API 提供了丹麦所有能源消费类型的每小时能源消耗值[1]。
它们提供了一个直观的界面,你可以轻松查询和可视化数据。你可以在这里访问数据 [1]。
数据具有 4 个主要属性:
-
小时 UTC: 数据点观察时的 UTC 日期时间。
-
价格区域: 丹麦被划分为两个价格区域:DK1 和 DK2——由大贝尔特海峡划分。DK1 位于大贝尔特海峡西侧,DK2 位于大贝尔特海峡东侧。
-
消费类型: 消费类型是由丹麦能源公司拥有和维护的行业代码 DE35。
-
总消耗: 总电力消耗(单位:千瓦时)
注意: 观察数据有 15 天的滞后!但对于我们的演示用例来说,这不是问题,因为我们可以模拟与实时相同的步骤。
我们的网络应用程序的屏幕截图,展示了我们如何预测区域 = 1 和消费类型 = 212 的能源消耗 [作者提供的图片]。
数据点具有每小时的分辨率。例如:“2023–04–15 21:00Z”,“2023–04–15 20:00Z”,“2023–04–15 19:00Z”等。
我们将数据建模为多个时间序列。每个唯一的价格区域和消费类型组合表示一个唯一的时间序列。
因此,我们将建立一个独立预测未来 24 小时每个时间序列的能源消耗的模型。
查看下面的视频,以更好地理解数据的样子 👇
课程与数据源概述 [作者提供的视频]。
课程 4:私人 PyPi 服务器。用 Airflow 协调一切。
第四部分课的目标
这节课将教你如何使用 Airflow 来协调你迄今为止实现的三个管道。
此外,要运行 Airflow 中的代码,你将学习如何托管你的 PiPy 仓库,并将管道部署为 3 个不同的 Python 模块。之后,你将直接从你的 PiPy 仓库中安装这些模块到 Airflow 中。
第 4 课组件用蓝色高亮的最终架构图 [作者提供的图片]。
通过使用 Airflow 编排所有内容,你将自动化整个过程。你不再需要手动运行 10 个不同的脚本,而只需点击一次“运行”按钮即可运行整个代码。
同时,以编程方式将所有步骤连接起来的程序更不容易出现错误。
为什么?
因为每个脚本都需要自己的配置。例如,批量预测管道需要特征视图版本(数据版本)和模型版本作为输入。
这些信息是从之前的脚本生成的元数据。当你手动运行一切时,容易复制错误的版本。但当你将所有内容封装在一个 DAG 中时,你只需构建一次,之后它将始终正常工作。
此外,通过使用 Airflow,你可以:
-
定期调度管道运行(你将每小时运行一次);
-
使用 Airflow 变量配置整个过程;
-
监控每个任务的日志。
这里是你将在 Airflow 中构建的概览 👇
理论概念与工具
Airflow: Airflow 是最受欢迎的编排工具之一。这个项目最初在 Airbnb 开发,但现在在 Apache 许可证下开源。这意味着你可以免费修改和托管它。Airflow 允许你构建、调度和监控 DAG。
DAG(有向无环图): DAG 是一种没有循环的图,这意味着逻辑流只能朝一个方向进行。
PyPi 注册表: PiPy 注册表是一个可以托管各种 Python 模块的服务器。当你运行“pip install <your_package>”时,pip 知道如何查看官方 PyPi 仓库中的你的包并安装它。托管自己的 PyPi 注册表的行为完全相同,但你必须配置 pip 以知道如何访问它。只有访问你 PyPi 服务器的人才能从中安装包。
第 4 课:代码
注意: 所有的安装说明都在仓库的 README 文件中。这里你将直接跳转到代码部分。
第 4 课中的所有代码都位于 airflow 文件夹下。
airflow 文件夹下的文件结构如下:
显示 airflow 文件夹结构的截图 [作者提供的图片]。
所有代码都位于dags 目录下**。** 每个 DAG 都有其自己的 Python 文件。
Docker 文件将帮助你快速托管 Airflow 和 PiPy 仓库。我会在后面详细解释。
直接将凭证存储在你的 git 仓库中是一个巨大的安全风险。这就是为什么你将使用**.env**文件来注入敏感信息。
.env.default是你必须配置的所有变量的示例。它也有助于存储不敏感的属性的默认值(例如,项目名称)。
.env.default 文件的截图 [作者提供的图片]。
准备凭证
由于第四部分课讨论了如何协调其他所有课程中的代码,如果你想重现代码,你需要检查如何设置第一部分、第二部分和第三部分中的 3 个管道。
这三节课将展示如何设置所有必要的工具和服务。还会展示如何创建并完成包含所有凭证的所需.env 文件。
.env.default 文件的截图 [作者提供的图片]。
唯一需要注意的是 👇
这次你需要将包含凭证的**.env文件放置在airflow/dags文件夹下。**
我们在 docker-compose.yaml 文件中为ML_PIPELINE_ROOT_DIR环境变量设置了默认值**/opt/airflow/dags**。因此,在 Airflow 内部运行管道时,它将默认从**/opt/airflow/dags加载.env**文件。
同时,请注意在/ airflow文件夹下还有另一个**.env**文件。这个文件不包含你的自定义凭证,但 Airflow 需要一些自定义配置。它的样子如下 👇
/airflow 文件夹中的.env 文件截图 [作者提供的图片]。
我在仓库的README.md中解释了如何完成这个**.env文件。但作为附注,AIRFLOW_UID代表你计算机的用户 ID,而你知道ML_PIPELINE_ROOT_DIR**是什么。
我只是想向你展示你可以在这里覆盖ML_PIPELINE_ROOT_DIR的默认值。请注意,这个路径将在 Docker 容器内使用,因此路径以**/opt/**开头。
# Move to the airflow directory.
cd airflow
# Make expected directories and environment variables
mkdir -p ./logs ./plugins
sudo chmod 777 ./logs ./plugins
# It will be used by Airflow to identify your user.
echo -e "AIRFLOW_UID=$(id -u)" > .env
# This shows where the project root directory is located.
echo "ML_PIPELINE_ROOT_DIR=/opt/airflow/dags" >> .env
设置私人 PyPi 服务器
你可以使用 这个仓库 容易地托管一个 PiPy 服务器。不过让我解释一下我们在设置中是如何做的。
第一步是创建一组凭证,这些凭证是你连接到 PyPi 服务器所需的。
# Install dependencies.
sudo apt install -y apache2-utils
pip install passlib
# Create the credentials under the energy-forecasting name.
mkdir ~/.htpasswd
htpasswd -sc ~/.htpasswd/htpasswd.txt energy-forecasting
PyPi 仓库将知道从 ~/.htpasswd/htpasswd.txt 文件中加载凭证。
现在,你将把新的私有 PyPi 仓库添加到 Poetry 中。要配置 Poetry,你需要指定服务器的 URL、服务器名称以及用于认证的用户名和密码(这些是你之前配置的):
poetry config repositories.my-pypi http://localhost
poetry config http-basic.my-pypi energy-forecasting <password>
在我们的示例中:
-
服务器名称: my-pypy
-
URL:
localhost
-
用户名: energy-forecasting
-
密码:
检查你的 Poetry auth.toml 文件中的凭证设置是否正确:
cat ~/.config/pypoetry/auth.toml
你已经准备好了用户名和密码,这些将被你的 PyPi 仓库用于认证。同时,你也配置了 Poetry 以识别你的 PyPi 服务器。
现在,让我们看看如何运行 PyPi 服务器。
你将使用的 pyserver 代码 已经进行了 Docker 化。
为了简化,我们将 PyPi 服务器作为额外的服务添加到运行 Airflow 应用程序的 docker-compose.yaml 文件中。
为了更好地理解 docker-compose.yaml 文件,请查看 Airflow 官方文档 [2] 和 我们的 README.md。但请注意使用我们仓库中的 docker-compose.yaml 文件,因为我们修改了原始文件,正如下文所示。
滚动到 airflow/docker-compose.yaml 文件的底部,你会看到:
my-private-pypi:
image: pypiserver/pypiserver:latest
restart: always
ports:
- "80:8080"
volumes:
- ~/.htpasswd:/data/.htpasswd
command:
- run
- -P
- .htpasswd/htpasswd.txt
- --overwrite
这段代码使用了 PyPi 服务器的最新镜像,将服务器暴露在 80 端口,将包含你凭证的 ~/.htpasswd 文件夹作为卷加载,并用以下命令运行服务器:
run -P .htpasswd/htpasswd.txt --overwrite
-
“-P .htpasswd/htpasswd.txt” 明确告诉服务器使用哪些凭证。
-
“— overwrite” 表示如果部署了相同版本的新模块,它将覆盖上一个版本。
就这样!当你运行 Airflow 应用程序时,你会自动启动 PyPi 服务器。
注意: 在生产环境中,你可能会将 PyPi 服务器托管在与 Airflow 不同的服务器上。步骤是相同的,只是将所有内容添加到单个 docker-compose.yaml 文件中。在本教程中,我们希望一切运行起来更加简单。
自定义 Airflow Docker 文件
由于你必须在 Python 3.9 中运行所有代码,因此你需要继承默认的 apache/airflow:2.5.2 Airflow Docker 镜像,并添加一些额外的依赖项。
以下是 Docker 文件中的内容:
-
继承 apache/airflow:2.5.2
-
切换到 root 用户以安装系统依赖项
-
安装从私有 PyPi 服务器安装包所需的 Python 3.9 依赖项
-
切换回默认用户
因为我们切换了:
x-airflow-common:
&airflow-common
image: ${AIRFLOW_IMAGE_NAME:-apache/airflow:2.5.2}
到:
version: '3.8'
x-airflow-common:
&airflow-common
# image: ${AIRFLOW_IMAGE_NAME:-apache/airflow:2.5.2}
build: .
Docker 会知道在运行 docker-compose 时使用你的自定义镜像,而不是apache/airflow:2.5.2。
运行 Airflow
现在你了解了如何准备凭据以及 Docker 文件如何工作,前往./airflow目录并运行:
# Go to the ./airflow directory.
cd ./airflow
# Initialize the Airflow database
docker compose up airflow-init
# Start up all services
# Note: You should set up the private PyPi server credentials before running this command.
docker compose --env-file .env up --build -d
完成 Airflow 设置后,你可以使用默认凭据在127.0.0.1:8080访问 Airflow:
-
用户名: airflow
-
密码: airflow
Airflow 登录页面的截图 [作者提供的图片]。
将模块部署到私有 PyPi 服务器
记住你使用以下命令将新的 PyPi 服务器添加到 Poetry 中:
poetry config repositories.my-pypi http://localhost
poetry config http-basic.my-pypi energy-forecasting <password>
现在,使用my-pypi作为标识符,你可以快速将新包推送到你的 PyPi 仓库。
使用deploy/ml-pipeline.sh脚本,你可以仅使用 Poetry 构建和部署所有 3 个管道:
!/bin/bash
# Build and publish the feature-pipeline, training-pipeline, and batch-prediction-pipeline packages.
# This is done so that the pipelines can be run from the CLI.
# The pipelines are executed in the feature-pipeline, training-pipeline, and batch-prediction-pipeline
# directories, so we must change directories before building and publishing the packages.
# The my-pypi repository must be defined in the project's poetry.toml file.
cd feature-pipeline
poetry build
poetry publish -r my-pypi
cd ../training-pipeline
poetry build
poetry publish -r my-pypi
cd ../batch-prediction-pipeline
poetry build
poetry publish -r my-pypi
如你所见,我们迭代地进入 3 个管道的文件夹并运行:
poetry build
poetry publish -r my-pypi
Poetry 使用这两个命令在文件夹中查找pyproject.toml和poetry.lock文件,并知道如何构建包。
然后,根据生成的wheel文件,运行**“poetry publish -r my-pypi”,你将其推送到你的my-pipy**仓库。
记住你将你的 PyPi 服务器标记为my-pipy。
完成了。你拥有了自己的 PyPi 仓库。
在接下来的章节中,我将向你展示如何从你的私有 PyPi 仓库中安装包。
注意: 你只使用 Poetry 来构建和部署模块。Airflow 将使用 pip 从你的 PyPi 仓库中安装这些模块。
定义 DAG 对象
你的 dag 定义在airflow/dags/ml_pipeline_dag.py文件中。
使用Airflow 2.0 的 API,你可以使用dag() Python 装饰器定义一个 DAG。
你的 dag 将在**ml_pipeline()**函数中定义,该函数在文件末尾调用。此外,Airflow 知道加载airflow/dags目录下定义的所有 DAG。
DAG 具有以下属性:
-
dag_id: DAG 的 ID
-
schedule: 它定义了 DAG 运行的频率
-
start_date: 根据给定的时间表,DAG 应该何时开始运行
-
catchup: 自动填补[start_date, 现在]之间的时间
-
tags: 标签 😄
-
max_active_runs: 此 DAG 可以并行运行的实例数
定义任务
下面的代码可能看起来很长,但一旦你理解了主要思路,它很容易阅读。
在 DAG 中,你定义了多个任务。一个任务是一个单独的逻辑单元/步骤,执行特定的操作。
任务的定义类似于 DAG:一个函数+一个装饰器。每个任务都有其函数和装饰器。
注意: 这是一个简单的提醒,我们使用的是 Airflow 2.0 的 API,而不是 1.0 的。
在我们的案例中,一个任务将代表一个主管道脚本。例如,特征工程管道将在一个单独的任务中运行。
你将使用 DAG 将所有脚本粘合在一个“程序”下,每个脚本与一个任务有 1:1 的对应关系。
"ml_pipeline"的视觉表示(请参见 YouTube 视频以获取更好的视图) [作者提供的图像]。
正如你在每个任务中看到的,你只需从其模块中导入并调用函数……并可能添加一些额外的日志。
定义任务的关键步骤在于task.virtualenv() Python 装饰器的参数中。
对于每个任务,这个特定的装饰器将创建一个不同的 Python 虚拟环境,在其中安装所有给定的需求。
注意: 172.17.0.1 是你私有 PyPi 仓库的 IP 地址。记住,你通过 docker-compose 在与 Airflow 相同的网络下托管你的 PyPi 仓库。172.17.0.1 是每个 Docker 容器在default Docker网络内可以访问的桥接 IP 地址。因此,Airflow 容器可以通过桥接 IP 地址访问 PyPi 服务器容器。
正如在requirements参数中所示,我们定义了以下内容:
-
“— trusted-host 172.17.0.1”: 由于 PyPi 服务器没有用 HTTPS 保护,你必须明确表示你信任这个来源。
-
“— extra-index-url http://172.17.0.1”: 告诉 Pip 在搜索 Python 包时也查看这个 PyPi 仓库。请注意,Pip 在搜索时仍会查看官方 PyPi 仓库。
-
“<your_python_packages>”: 在上述两行之后,你可以添加任何 Python 包。但请注意,你已经安装了feature_pipeline、training_pipeline和batch_prediction_pipeline作为你用 Poetry 构建和部署的 Python 包。
其他参数并不是那么有趣,但让我来解释一下:
-
task_id=" <task_id>": 任务的唯一 ID。
-
python_version=" 3.9": 当我写这门课程时,Hopsworks 只支持 Python 3.9,所以我们必须强制使用这个版本的 Python。
-
multiple_outputs=True: 任务返回一个 Python 字典。
-
system_site_packages=True: 安装默认的系统包。
重要
注意,几乎每个任务都会返回一个包含信息的元数据字典,例如:
-
数据提取的日期范围,
-
特征组、特征视图等的版本。
-
sweep 的版本。
-
模型的版本等。
这些信息在任务之间传递是至关重要的。例如,create_feature_view 任务需要知道使用哪个版本的 feature_group 来创建下一个特征视图。此外,在运行 batch_predict 时,你需要知道要使用哪个版本的特征视图和模型来生成预测。
一个有趣的任务是 task.branch(task_id=" if_run_hyperparameter_tuning_branching"),它定义了一个是否运行超参数调整逻辑的 if-else 逻辑。
这种特殊类型的任务返回一个 task_ids 列表,这些任务将会被执行。例如,如果它返回 [“branch_run_hyperparameter_tuning”],则仅运行 task_id = branch_run_hyperparameter_tuning 的任务。
如下所示,定义了两个空的操作符(任务),并在 task.branch() 逻辑中使用了 task_ids。这是 Airflow 建议的一种常见模式,当在多个分支之间进行选择时,使用一组空操作符(无操作)。
将任务连接成一个 DAG
现在你已经定义了所有的任务,最后一步是将它们连接成一个 DAG。你必须执行这一步,以便 Airflow 知道每个任务的运行顺序。
基本上,这里你将定义逻辑图。
#1. 第一步是 确定你将用于配置 DAG 的变量集合,如 days_delay, days_export, feature_group_version, 等。你可以从 Airflow 的“Admin -> Variables”面板中访问这些变量。
请注意,你必须显式地使用蓝色加号按钮添加它们。
变量 Airflow 面板的截图 [作者提供的图片]。
#2. 第二步是 调用具有正确参数的任务。如你所见,由于 Airflow 2.0 API,这一步就像按特定顺序调用一系列 Python 函数一样。
注意: 如果一个函数的输出作为输入添加到另一个函数中,则图中的依赖关系会自动创建。
强调如何将每个管道元素的元数据传递到下一个元素是至关重要的。这样,我们强制执行以下脚本,以使用正确的数据和模型版本。
我还想强调以下这段代码:
feature_pipeline_metadata = run_feature_pipeline(
export_end_reference_datetime="{{ dag_run.logical_date }}",
)
“{{ dag_run.logical_date }}" 是 Airflow 注入的模板变量,反映了 DAG 运行时的逻辑日期,而不是当前日期。通过这样做,利用 Airflow 回填功能,你可以轻松地将其作为日期时间参考来回填给定时间窗口中的数据。现在你可以轻松地操作提取窗口的起始和结束点。
例如,如果你想运行 DAG 以回填 2023 年 5 月 10 日至 11 日的能源消耗预测,你将使用“2023 年 5 月 10 日 00:00 am”日期运行 Airflow 回填逻辑。
#3. 最后一步是使用 ">>" 操作符来强制执行特定的 DAG 结构。
“A >> B >> C” 意味着先运行 A,然后 B,再运行 C。
唯一稍微复杂一点的代码是这个:
>> if_run_hyperparameter_tuning_branch
>> [
if_run_hyperparameter_tuning_branch
>> Label("Run HPO")
>> branch_run_hyperparameter_tuning_operator
>> last_sweep_metadata
>> upload_best_model_step,
if_run_hyperparameter_tuning_branch
>> Label("Skip HPO")
>> branch_skip_hyperparameter_tuning_operator,
]
,其中根据branch运算符,DAG 将运行branch_run_hyperparameter_tuning_operator或branch_skip_hyperparameter_tuning_operator分支。
阅读有关 Airflow 中分支的更多信息这里 [3]。
以英语运行时,它将进行超参数优化或跳过,如下图所示——我知道图片比较小。查看视频以获取更清晰的视图。 👇
ml_pipeline DAG 截图 [作者提供的图片]。
就这样!你使用 Airflow 协调了所有 3 个管道。恭喜!
运行 ML Pipeline DAG
这一步很简单。
只需进入你的ml_pipeline DAG 并点击播放按钮。
ml_pipeline DAG 视图截图 [作者提供的图片]。
使用 Airflow 进行回填
查找你的airflow-webserver docker 容器 ID:
docker ps
在airflow-webserver容器内启动 shell 并运行airflow dags backfill,如下所示:
docker exec -it <container-id-of-airflow-webserver> sh
# In this example, you did a backfill between 2023/04/11 00:00:00 and 2023/04/13 23:59:59.
airflow dags backfill --start-date "2023/04/11 00:00:00" --end-date "2023/04/13 23:59:59" ml_pipeline
如果你想清除任务并重新运行它们,请运行以下命令:
docker exec -it <container-id-of-airflow-airflow-webserver> sh
airflow tasks clear --start-date "2023/04/11 00:00:00" --end-date "2023/04/13 23:59:59" ml_pipeline
结论
恭喜!你完成了第四课来自全栈 7 步 MLOps 框架课程。
如果你已经看到这里,你知道如何:
-
托管自己的 PyPi 服务器
-
使用 Poetry 构建和部署 Python 模块
-
使用 Airflow 协调多个管道
现在你已经理解了使用像 Airflow 这样的协调工具的强大功能,你可以构建强大的生产就绪管道,并快速调度、配置和监控。
查看第 5 课了解如何使用 Great Expectations 验证数据的完整性和质量。此外,你将了解如何在机器学习系统上实现监控组件。
此外, 你可以在这里访问 GitHub 仓库。
💡 我的目标是帮助机器学习工程师提升设计和生产机器学习系统的能力。关注我在LinkedIn或订阅我的每周通讯以获取更多见解!
🔥 如果你喜欢阅读这样的文章并希望支持我的写作,考虑成为 Medium 会员。通过使用我的推荐链接,你可以在不增加任何额外费用的情况下支持我,同时享受 Medium 丰富故事的无限访问权限。
[## 使用我的推荐链接加入 Medium - Paul Iusztin
🤖 加入以获取有关设计和构建生产就绪机器学习系统的独家内容 🚀 解锁全部访问权限…
参考资料
[1] 丹麦 API 每小时 DE35 行业代码的能耗,丹麦能源数据服务
[2] 在 Docker 中运行 Airflow,Airflow 文档
[3] Airflow 中的分支,Astronomer 上的 Airflow 文档
释放 JupyterLab 的潜力:发现你从未知道过的强大文本编辑器
在 JupyterLab 中通过一个优秀的文本编辑器释放你的编码效率和生产力
·发布在 Towards Data Science ·6 分钟阅读·2023 年 3 月 1 日
–
图片由 Luca Bravo 提供,发布在 Unsplash
本文是一个系列的第一部分。查看完整系列:第二部分、第三部分、第四部分。
JupyterLab 是一个开源的基于网页的交互式编码环境,许多人认为它并不是一个完整的 IDE。支持这一观点的众多原因之一是 JupyterLab 缺乏一个强大的文本编辑器。
全面性、让你保持专注的工具和教育是 2022 年 Project Jupyter 的主要主题
towardsdatascience.com
JupyterLab 允许用户创建和分享包含实时代码、方程式、可视化和叙述性文本的文档。它提供了一个灵活而强大的平台,用于科学计算、数据分析和机器学习工作流,同时支持用户在各种环境中运行代码,从本地机器到远程服务器和云服务。
然而,许多数据科学家和工程师仅仅将其用于实验。当需要将他们的工作投入生产时,他们会转向 IDE 来将所有部分整合起来。
现如今最受欢迎的 IDE 是 Microsoft Visual Studio Code。VS Code 等工具提供了一个强大的文本编辑器、一个调试器以及多个插件,使得使用 Git、Docker 或几乎任何编程语言变得非常轻松。但如果我告诉你,你可以在 JupyterLab 中获得类似的体验,你会怎么想?
在这一系列文章中,我们将会在 JupyterLab 中打造一个类似于 VC Code 的体验,并创建一个 Docker 镜像,这样我们就可以在任何地方使用它作为我们的自定义工作区。让我们开始吧!
这个故事的第二部分已经发布:
## Jupyter 已经有一个完美的文本编辑器:这就是你可以如何配置它
如何在 Jupyter 中获得类似 VS Code 的体验以及一个出色的文本编辑器
[towardsdatascience.com
Learning Rate 是一本针对对机器学习和 MLOps 领域感兴趣的人的新闻通讯。如果你想了解更多类似的主题,可以点击这里订阅。每个月的最后一个星期日,你将会收到我关于最新 MLOps 新闻和文章的更新和想法!
你为什么需要关心?
在我们开始之前,让我们先回答一个问题:你为什么需要关心这个问题?你已经有了 Visual Studio Code;为什么还要在 JupyterLab 中构建类似的体验?此外,VS Code 已经为 Jupyter Notebooks 提供了一个很好的编辑器。事实上,我通常觉得 VS Code 的 Notebooks UI 更好。
好吧,如果你本地编程,从不通过 ssh 连接到远程机器或使用不同的工作站来完成工作,那么你可能不需要这个。
然而,如今大多数人需要连接到远程服务器才能完成任务。例如,我使用 Kubeflow 来设计和运行我的 ML 实验和管道。Kubeflow 中的编码环境通常是一个 Jupyter 服务器。
那么,在这种情况下你会怎么做?拥有一个 VS Code 服务器可能是具有挑战性的。你要么需要付费订阅,要么学会接受code server(尽管是一个很棒的项目)的局限性。有没有替代方案,最好是开源的?
这个故事是为你准备的。在这一系列的最后,我们将在 JupyterLab 中建立一个强大的 Python 编码环境。将其扩展以支持更多编程语言将是微不足道的。
Jupyter 已经有了一个完美的文本编辑器
可能会让人惊讶的是,JupyterLab 已经有一个很棒的文本编辑器。为什么呢?因为 Jupyter 有一个终端模拟器;因此,我们可以安装 Neovim。然后,我们可以将 Neovim 配置为我们想要的样子和行为。
本系列文章的终极目标是安装 Neovim,并使其像 Python IDE 一样工作。相信我,到最后,你将拥有从 VS Code 迁移到通用文本编辑器所需的核心组件,一个可以带到任何地方的编辑器。
完成的体验大致如下:
图片来自作者
我们看到一个完整的 IDE 体验,包含文件浏览器、代码大纲、集成终端、代码检查和带有代码补全和文档弹出的 IntelliSense!我们甚至有像美丽的状态栏和根据文件内容显示图标等小特性。
在未来的文章中,我们将探索每一个隐藏的宝石,比如多行注释、拼写检查器、窗格导航以及以 VS Code 原生方式配置插件。首先,让我们安装 Neovim 并创建我们的初始配置。
在 JupyterLab 中安装 Neovim
在 Jupyter 中创建类似 VS Code 的体验的第一步是运行一个可以配置的 Jupyter 服务器。我们将使用上游的数据科学镜像,从 DockerHub 拉取。因此,准备好后,请运行以下命令:
docker run --name jupyter \
-p 8888:8888 \
--user root \
-e GRANT_SUDO=yes \
jupyter/datascience-notebook:latest
我们需要 sudo
权限,因为我们将安装各种 Debian 软件包和插件。我们传递的参数将授予默认用户(jovyan
)无密码的 sudo
权限。有关更多信息,请参见 文档。
接下来,在 JupyterLab 中启动终端并安装最新版本的 Neovim:
wget https://github.com/neovim/neovim/releases/download/stable/nvim-linux64.deb && sudo dpkg -i nvim-linux64.deb
为了验证一切是否如预期那样工作,请运行 nvim
。在撰写本文时,最新的稳定版本是 v0.8.3
:
图片来自作者
学习 Vim
Neovim 基于 Vim,是一个强大且高度可定制的文本编辑器,主要用于类 Unix 操作系统。它设计为完全通过键盘使用,命令基于一系列按键和助记符缩写的组合。
那么,为什么选择 Neovim 而不是 Vim 呢?Neovim 是 Vim 的一个分支,这意味着它基于与 Vim 相同的核心代码,但具有额外的功能和改进。Neovim 的主要目标是提供更好的扩展性、更现代的开发实践和比 Vim 更可维护的代码库。
Neovim 相对于 Vim 有许多优点,但列举这些优点并不是本文的目标。不过,如果你知道如何在 Vim 中导航,你可以将这些知识转移到 Neovim 中。因此,让我们看看如何学习 Vim!
网上有很多资源;然而,在我看来,最好的方式是 vimtutor
。首先,安装 Vim:
sudo apt install vim
然后,在终端中运行 vimtutor
:
图片来自作者
vimtutor
有七个课程,涵盖了 Vim 的基本概念。完成这个互动教程将为你提供开始使用 Vim 和 Neovim 所需的所有工具。
在这篇文章中,我留给你们一句话:学习你的 Vim 绑定!如果你忘记了一些按键,不用担心;练习会让一切变得更简单。
在接下来的文章中,我们将开始配置 Neovim。我们将从配置编辑器的核心功能和外观开始,然后深入探讨更多专业插件,这些插件将把我们的编辑器转变为功能丰富的 Python IDE。
关于作者
我叫 Dimitris Poulopoulos,是一名在 Arrikto 工作的机器学习工程师。我为欧洲委员会、Eurostat、国际货币基金组织、欧洲中央银行、OECD 和宜家等主要客户设计和实施了 AI 和软件解决方案。
如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和 DataOps 的文章,可以在 Medium、LinkedIn 或 Twitter 上关注 @james2pl。
表达的观点仅代表我个人,不代表我雇主的观点或意见。
解锁大数据的力量:图学习的迷人世界
利用深度学习将未开发的数据转变为长期竞争力的战略资产。
·
关注 发表在 Towards Data Science ·12 分钟阅读·2023 年 11 月 9 日
–
图片由 Nathan Anderson 提供,Unsplash 上的照片
大公司生成和收集大量数据,以一个例子来说,90% 的数据是在最近几年创建的。然而,73% 的这些数据仍然未被使用 [1]。然而,正如你可能知道的那样,数据是从事大数据工作的公司的金矿。
深度学习不断发展,今天的挑战是将这些新解决方案调整到特定目标,以突出表现并增强长期竞争力。
我以前的经理很有直觉,认为这两个事件可以结合在一起,共同促进访问、请求,最重要的是避免浪费时间和金钱。
为什么这些数据被闲置?
访问这些数据需要很长时间,权利验证,特别是内容检查在授予用户访问权限之前是必需的。
可视化数据未被使用的原因。(由 Bing Image Creator 生成)
有没有自动记录新数据的解决方案?
如果你对大型企业不太熟悉,也没关系——我也是。这样环境中的一个有趣概念是使用大数据,特别是HDFS(Hadoop 分布式文件系统),它是一个旨在整合公司所有数据的集群。在这庞大的数据池中,你可以找到结构化数据,而在这些结构化数据中,Hive 列被引用。这些列中的一些用于创建附加表,并可能作为各种数据集的来源。公司通过数据血缘保持表与表之间的信息。
这些列还具有各种特征(领域、类型、名称、日期、所有者等)。项目的目标是用业务数据来记录被称为物理数据的数据。
区分物理数据和业务数据:
简而言之,物理数据是表格中的列名,而业务数据则是该列的使用方式。
例如:名为 Friends 的表格包含列(角色、薪资、地址)。我们的物理数据是角色、薪资和地址。我们的业务数据例如,
-
对于“Character” -> 角色的名称
-
对于“Salary” -> 薪资金额
-
对于“Address” -> 人的地址
这些业务数据将帮助访问数据,因为你可以直接获得所需的信息。你会知道这是你项目所需的数据集,你寻找的信息 在这个表格中。所以你只需请求并找到你的幸福,提前去而不浪费时间和金钱。
“在我的最后一次实习中,我和我的团队 实习生*,实现了一个大数据/图学习解决方案来记录这些数据。*
这个想法是创建一个图来结构化我们的数据,并最终根据特征预测业务数据。换句话说,从存储在公司环境中的数据中,记录每个数据集以关联用途,将来减少搜索成本,并更具数据驱动。
我们有 830 个标签需要分类,但行数不多。希望图学习的力量能发挥作用。我让你阅读…“
文章目标: 本文旨在提供对大数据概念、图学习、使用的算法及结果的理解。它还涉及部署考虑事项以及如何成功开发模型。
为了帮助你理解我的过程,本文的提纲包括:
-
数据采集:为图创建获取必要数据
-
基于图的建模与 GSage
-
有效的部署策略
数据采集
正如我之前提到的,数据通常存储在 Hive 列中。如果你还不知道,这些数据存储在大型容器中。我们通过称为 ETL 的技术提取、转换和加载这些数据。
我需要什么类型的数据?
-
物理数据及其特征(领域、名称、数据类型)。
-
谱系(物理数据之间的关系,如果它们经历了共同的转换)。
-
将“某些与业务数据相关的物理数据”进行映射,然后“让”算法自行执行。
1. 特征/特性在我们存储数据时直接获得;它们在我们存储数据时是必需的。例如(取决于你的情况):
主要特征的示例,(由作者提供)
对于特征,基于经验,我们决定对三列使用特征哈希器。
特征哈希器: 机器学习中用于将高维度的分类数据(如文本或分类变量)转换为低维度数值表示的技术,以减少内存和计算需求,同时保留有意义的信息。
如果你有类似的模式,可以选择独热编码技术。如果你想交付你的模型,我建议使用特征哈希器。
2. 谱系稍微复杂一些,但并非不可理解。谱系就像物理数据的历史,我们大致了解已应用的转换以及数据存储在其他地方的位置。
想象一下你脑海中的大数据以及所有这些数据。在一些项目中,我们使用表格中的数据,并通过一个任务(Spark)进行转换。
从 Atlas 网站可视化的 Atlas 谱系,LINK
我们收集了所有物理数据的信息,以便在我们的图中创建连接,或者至少创建其中一个连接。
3. 映射是为我们的项目增值的基础。在这里,我们将业务数据与物理数据关联起来。这为算法提供了经过验证的信息,使其能够最终对新进来的数据进行分类。这个映射必须由了解公司流程的人完成,并且具备识别复杂模式的技能,而无需询问。
ML 建议,基于我自己的经验:
引用 Andrew NG 先生的话,在经典机器学习中,有一种叫做算法生命周期的东西。我们常常考虑算法,使其复杂,而不只是使用一个老式的线性回归(我尝试过;它不起作用)。在这个生命周期中,包括所有的预处理、建模和监控阶段……但最重要的是,数据聚焦。
这是我们经常犯的错误; 我们理所当然地开始进行数据分析。我们从数据集中得出结论,而有时却没有质疑其相关性。不要忘记数据聚焦,我的朋友们;它可以提升你的表现,甚至导致项目的改变 😃
回到我们的文章,获取数据后,我们最终可以创建我们的图。
我们数据集的分布图的绘图(使用networkx制作)。(由作者制作)
该图考虑了一批 2000 行的数据,因此在数据集和表格中有 2000 列。你可以在中心找到业务数据,而偏离中心的是物理数据。
在数学中,我们将图表示为 G,G(N, V, f)。N 代表节点,V 代表顶点(边),f 代表特征。假设这三者都是非空集合。
对于节点(我们在映射表中有业务数据 ID)以及物理数据,以便通过谱系追踪它们。
谈到谱系,它部分作为边,通过映射和 ID 链接我们已有的链接。我们必须通过使用Apache Atlas API的 ETL 过程来提取它。
你可以看到,在奠定基础之后,大数据问题可以变得容易理解,但实施起来却更具挑战性,尤其对于一名年轻的实习生来说……
“计算机上的忍者卡通”(由 Dall.E 3 生成)
基于图的建模与 GSage
图学习基础
本节将致力于解释 GSage 以及为何在数学和经验上都选择了它。
在这次实习之前,我不习惯使用图形。这就是为什么我购买了书籍**[2]**,我在描述中包含了它,因为它大大帮助我理解了原理。
[## 图机器学习:通过应用机器学习技术将图数据提升到一个新的水平……
注意 /5。请查阅《图机器学习:通过应用机器学习技术将图数据提升到一个新的水平》……
原理很简单:当我们谈论图学习时,我们必然会讨论嵌入。在这个背景下,节点及其邻近关系在数学上被转换为减少原始数据集维度的系数,使计算更加高效。在降维过程中,解码器的一个关键原则是保持初始接近的节点之间的邻近关系。
另一个灵感来源是 Maxime Labonne [3],他对 GraphSages 和图卷积网络的解释展示了极佳的教学法,提供了清晰易懂的例子,使这些概念对希望深入了解的人变得更加易于理解。
GraphSage 的模型
如果这个术语对你来说不熟悉,请放心,几个月前我也在你的位置。像注意力网络和图卷积网络这样的架构曾让我经历了不少噩梦,更重要的是,它们让我夜不能寐。
为了节省你整天的时间,尤其是你的通勤时间,我将为你简化算法。
一旦你有了嵌入,这时候魔法才会发生。但你问一切是如何运作的?
基于《史酷比宇宙》的图解来解释 GSage(由作者制作)。
“你以交友闻名”这句话,你必须记住。
因为 GraphSAGE 的一个基本假设是同一邻域中的节点应该表现出类似的嵌入。为实现这一点,GraphSAGE 使用聚合函数,将邻域作为输入,并结合每个邻居的嵌入及特定权重。这就是为什么神秘公司嵌入会出现在 Scooby 的邻域中的原因。
本质上,它从邻域收集信息,权重可以是学习得出的,也可以是固定的,具体取决于损失函数。
当聚合器权重被学习时,GraphSAGE 的真正实力便会显现。此时,该架构可以利用节点的特征和邻域为未见节点生成嵌入,使其成为图基机器学习中各种应用的强大工具。
架构训练时间的差异,Maxime Labonne 的文章,链接
如你在此图中所见,当我们在 GraphSage 架构上使用相同的数据集时,训练时间会减少。GAT(图注意网络)和 GCN(图卷积网络)也是非常有趣的图架构。我真的鼓励你关注!
第一次计算时,我感到震惊,震惊于看到训练 1000 批次在数千行数据上仅需 25 秒。
我知道你现在对图学习感兴趣,并想了解更多,我的建议是阅读这位作者的内容。提供了很好的示例和建议。
使用 PyTorch Geometric 的 GraphSAGE 简介
[
作为 Medium 的读者,当我查看一篇新文章时,我会好奇阅读代码,对于你来说,我们可以在 PyTorch Geometric 中实现 GraphSAGE 架构,使用SAGEConv
层。
让我们创建一个包含两个SAGEConv
层的网络:
-
第一个使用了ReLU作为激活函数和一个dropout 层;
-
第二个直接输出节点嵌入。
在我们的多类分类任务中,我们选择使用交叉熵损失作为主要损失函数。这一选择是由于其适用于具有多个类别的分类问题。此外,我们还采用了强度为 0.0005 的 L2 正则化。
这种正则化技术有助于防止过拟合,通过对大参数值进行惩罚来促进模型的泛化。这是一种确保模型稳定性和预测准确性的全面方法。
import torch
from torch.nn import Linear, Dropout
from torch_geometric.nn import SAGEConv, GATv2Conv, GCNConv
import torch.nn.functional as F
class GraphSAGE(torch.nn.Module):
"""GraphSAGE"""
def __init__(self, dim_in, dim_h, dim_out):
super().__init__()
self.sage1 = SAGEConv(dim_in, dim_h)
self.sage2 = SAGEConv(dim_h, dim_out)#830 for my case
self.optimizer = torch.optim.Adam(self.parameters(),
lr=0.01,
weight_decay=5e-4)
def forward(self, x, edge_index):
h = self.sage1(x, edge_index).relu()
h = F.dropout(h, p=0.5, training=self.training)
h = self.sage2(h, edge_index)
return F.log_softmax(h, dim=1)
def fit(self, data, epochs):
criterion = torch.nn.CrossEntropyLoss()
optimizer = self.optimizer
self.train()
for epoch in range(epochs+1):
total_loss = 0
acc = 0
val_loss = 0
val_acc = 0
# Train on batches
for batch in train_loader:
optimizer.zero_grad()
out = self(batch.x, batch.edge_index)
loss = criterion(out[batch.train_mask], batch.y[batch.train_mask])
total_loss += loss
acc += accuracy(out[batch.train_mask].argmax(dim=1),
batch.y[batch.train_mask])
loss.backward()
optimizer.step()
# Validation
val_loss += criterion(out[batch.val_mask], batch.y[batch.val_mask])
val_acc += accuracy(out[batch.val_mask].argmax(dim=1),
batch.y[batch.val_mask])
# Print metrics every 10 epochs
if(epoch % 10 == 0):
print(f'Epoch {epoch:>3} | Train Loss: {total_loss/len(train_loader):.3f} '
f'| Train Acc: {acc/len(train_loader)*100:>6.2f}% | Val Loss: '
f'{val_loss/len(train_loader):.2f} | Val Acc: '
f'{val_acc/len(train_loader)*100:.2f}%')
def accuracy(pred_y, y):
"""Calculate accuracy."""
return ((pred_y == y).sum() / len(y)).item()
@torch.no_grad()
def test(model, data):
"""Evaluate the model on test set and print the accuracy score."""
model.eval()
out = model(data.x, data.edge_index)
acc = accuracy(out.argmax(dim=1)[data.test_mask], data.y[data.test_mask])
return acc
模型的部署:
在我们项目的开发和部署过程中,我们利用了三种关键技术,每种技术都有其独特而重要的作用:
来自Google的三个标志
Airflow: 为了高效管理和调度我们项目复杂的数据工作流,我们使用了 Airflow Orchestrator。Airflow 是一个广泛采用的任务调度工具,可以自动化流程,并确保我们的数据管道平稳且按时运行。
Mirantis: 我们项目的基础设施是在 Mirantis 云平台上构建和托管的。Mirantis 以提供强大、可扩展和可靠的云解决方案而闻名,为我们的部署提供了坚实的基础。
Jenkins: 为了简化我们的开发和部署流程,我们依赖 Jenkins,一个在持续集成和持续交付(CI/CD)领域中值得信赖的名字。Jenkins 自动化了项目的构建、测试和部署,确保了我们开发周期中的效率和可靠性。
此外,我们将我们的机器学习代码存储在公司的 Artifactory 中。 但什么是 Artifactory?
Artifactory: Artifactory 是一个集中式仓库管理器,用于存储、管理和分发各种工件,如代码、库和依赖项。它作为一个 安全 和有组织的存储空间,确保所有团队成员都可以轻松访问所需的资产。这使得 协作无缝并简化了应用程序和项目的部署,使其成为高效开发和部署工作流程的宝贵资产。
通过将我们的机器学习代码存储在 Artifactory 中,我们确保了我们的模型和数据 可以轻松支持通过 Jenkins 部署。
ET VOILA ! 解决方案已部署。
我谈了很多关于基础设施的内容,但对机器学习和我们取得的结果讲得不多。
结果 :
预测的可信度 :
对于每个物理数据,我们考虑了 2 个预测,因为模型性能的原因。
这怎么可能?
probabilities = torch.softmax(raw_output, dim = 1)
#torch.topk to get the top 3 probabilites and their indices for each prediction
topk_values, topk_indices = torch.topk(probabilities, k = 2, dim = 1)
首先,我使用了一个 softmax 函数来使输出可比,然后使用了一个名为 torch.topk 的函数。它返回给定 input
张量在指定维度上的 k
个最大元素。
那么,回到第一次预测,这里是我们训练后的分布。告诉你们,真棒!
模型输出的概率的绘图(来自 matplotlib),第一次预测(由作者制作)
训练/测试/验证中的准确率和损失。
我不会教你什么是机器学习中的准确率和损失,我假设你们都是专家……(如果不确定,可以询问 ChatGPT,没有羞耻感)。在训练中,通过不同的尺度,你可以看到曲线上的收敛,这很好,说明学习稳定。
准确率和损失的绘图(matplotlib)(由作者制作)
t-SNE :
t-SNE(t-分布随机邻域嵌入)是一种降维技术,用于通过在较低维空间中保留数据点之间的成对相似性来可视化和探索高维数据。
换句话说,想象一下训练前的随机分布 :
数据分布 训练前,(由作者制作)
记住我们正在做多分类,因此这是训练后的分布。特征的聚合似乎做得很满意。聚类形成,物理数据似乎已加入组,表明训练进行了良好。
数据分布 训练后,(由作者制作)
结论 :
我们的目标是基于物理数据预测业务数据(而且我们做到了)。我很高兴地通知你,该算法现在已投入生产,并正在为未来的用户进行接入。
虽然由于专有原因我不能提供完整的解决方案,但我相信你已经掌握了所有必要的细节,或者你完全有能力自行实施。
我最后的一条建议,我发誓,拥有一个出色的团队,不仅是那些工作出色的人,还有那些每天让你开心的人。
如果你有任何问题,请随时联系我。随时与我联系,我们可以详细讨论。
如果我没见到你,下午好,晚上好,晚安!
你掌握了吗?
正如钱德勒·宾可能会说:
“说谎总比进行复杂的讨论要好”
别忘了点赞和分享!
参考资料和资源
[1] Inc(2018),来自 Inc 的网络文章
[2] 图机器学习:通过应用机器学习技术和算法将图数据提升到一个新水平(2021),Claudio Stamile
[3] GraphSAGE,扩展图神经网络,(2021),Maxime Labonne
图片来源
-
Nathan Anderson的照片,来源于Unsplash
-
GraphSAGE 时间差,来自 Maxime Labonne 的文章,链接
-
Atlas Lineage 可视化,来自 Atlas 网站,链接
-
三个标志来自Google
媒体中面部模糊的力量解锁:全面探索与模型比较
各种人脸检测和模糊算法的比较
·
关注 发表在 Towards Data Science ·12 min read·2023 年 9 月 18 日
–
处理过的照片由 OSPAN ALI 提供,来源于 Unsplash
在当今数据驱动的世界中,确保个人隐私和匿名性至关重要。从保护个人身份到遵守严格的法规,如 GDPR,对各种媒体格式中人脸匿名化的高效可靠解决方案的需求从未如此迫切。
内容
-
介绍
-
人脸检测
-
Haar Cascade
-
MTCNN
-
YOLO
-
-
人脸模糊
-
高斯模糊
-
像素化
-
-
结果与讨论
-
实时性能
-
基于场景的评估
-
隐私
-
-
视频中的使用
-
Web 应用程序
-
结论
介绍
在这个项目中,我们探讨并比较了多种人脸模糊解决方案,并开发了一个允许轻松评估的 web 应用程序。让我们深入了解推动对这种系统需求的多样化应用:
-
保护隐私
-
导航法规环境:随着法规环境的迅速变化,全球各地的行业和地区正在实施更严格的规范,以保护个人身份。
-
训练数据保密性:机器学习模型依赖于多样化且准备充分的训练数据。然而,分享这些数据通常需要仔细的匿名化处理。
这个解决方案可以归结为两个基本组成部分:
-
人脸检测
-
人脸模糊技术
人脸检测
为了解决匿名化挑战,第一步是定位图像中存在人脸的区域。为此,我测试了三个图像检测模型。
Haar Cascade
图 1. 类似 Haar 的特征 (source — 原始论文)
Haar Cascade 是一种用于图像或视频中物体检测的机器学习方法,如人脸。它通过利用一组训练过的特征,称为‘类似 Haar 的特征’(图 1),这些特征是简单的矩形滤波器,集中于图像区域内像素强度的变化。这些特征可以捕捉边缘、角度及其他在脸部常见的特征。
训练过程包括向算法提供正面示例(包含人脸的图像)和负面示例(不包含人脸的图像)。算法通过调整特征的权重来学习区分这些示例。训练后,Haar Cascade 本质上成为一个分类器的层级结构,每个阶段逐步细化检测过程。
对于人脸检测,我使用了一个在正面人脸图像上训练的预训练 Haar Cascade 模型。
import cv2
face_cascade = cv2.CascadeClassifier('./configs/haarcascade_frontalface_default.xml')
def haar(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
print(len(faces) + " total faces detected.")
for (x, y, w, h) in faces:
print(f"Face detected in the box {x} {y} {x+w} {y+h}")
MTCNN
图 2. MTCNN 中的人脸检测过程 (source — 原始论文)
MTCNN(多任务级联卷积网络)是一个复杂且高度准确的人脸检测算法,超越了 Haar Cascades 的能力。MTCNN 设计用于在具有多样化面部尺寸、方向和光照条件的场景中表现优异,利用一系列神经网络,每个网络都专门执行人脸检测过程中的特定任务。
-
第一阶段 — 提议生成:MTCNN 通过一个小型神经网络开始生成大量潜在人脸区域(边界框)的过程。
-
第二阶段 — 精细化:在第一阶段生成的候选区域在此步骤中进行筛选。第二个神经网络评估提议的边界框,并调整其位置,以更精确地对齐真实的人脸边界。这有助于提高准确性。
-
第三阶段 — 面部特征点:此阶段识别面部标志点,如眼角、鼻子和嘴巴。使用神经网络准确定位这些特征。
MTCNN 的级联架构使其能够在过程早期迅速剔除没有人脸的区域,将计算集中在更可能包含人脸的区域。它处理不同尺度(缩放级别)的人脸和旋转的能力使其相比 Haar Cascades 更适用于复杂场景。然而,其计算强度源于其基于神经网络的顺序处理方法。
在实施 MTCNN 时,我使用了 mtcnn 库。
import cv2
from mtcnn import MTCNN
detector = MTCNN()
def mtcnn_detector(image):
faces = detector.detect_faces(image)
print(len(faces) + " total faces detected.")
for face in faces:
x, y, w, h = face['box']
print(f"Face detected in the box {x} {y} {x+w} {y+h}")
YOLOv5
图 3. YOLO 物体检测过程 (source — 原始论文)
YOLO(You Only Look Once)是一种用于检测多种物体的算法,包括人脸。与其前身不同,YOLO 在通过神经网络的单次传递中进行检测,使其速度更快,更适合实时应用和视频。使用 YOLO 在媒体中检测人脸的过程可以分为四个部分:
-
图像网格划分:输入图像被划分为一个网格单元。每个单元负责预测位于其边界内的物体。对于每个单元,YOLO 预测边界框、物体概率和类别概率。
-
边界框预测:在每个单元内,YOLO 预测一个或多个边界框及其对应的概率。这些边界框代表潜在的物体位置。每个边界框由其中心坐标、宽度、高度以及物体存在于该边界框内的概率定义。
-
类别预测:对于每个边界框,YOLO 预测物体可能属于的各种类别的概率(例如,“人脸”,“汽车”,“狗”)。
-
非最大抑制(NMS):为了消除重复的边界框,YOLO 应用 NMS。这个过程通过评估边界框的概率和与其他框的重叠情况来丢弃冗余的边界框,只保留最有信心且没有重叠的框。
YOLO 的主要优势在于其速度。由于它通过神经网络进行一次前向传播来处理整个图像,因此比涉及滑动窗口或区域提议的算法快得多。然而,这种速度可能会在精度上有些妥协,尤其是在较小的物体或拥挤的场景中。
YOLO 可以通过在面部特定数据上进行训练并将输出类别修改为仅包括一个类别(‘face’)来适应面部检测。为此,我利用了基于 YOLOv5 构建的‘yoloface’库。
import cv2
from yoloface import face_analysis
face=face_analysis()
def yolo_face_detection(image):
img,box,conf=face.face_detection(image, model='tiny')
print(len(box) + " total faces detected.")
for i in range(len(box)):
x, y, h, w = box[i]
print(f"Face detected in the box {x} {y} {x+w} {y+h}")
面部模糊
在图像中识别出潜在面部的边界框后,下一步是对它们进行模糊处理以去除其身份信息。为此任务,我开发了两种实现方法。图 4 提供了一个演示用的参考图像。
图 4. 参考图像,来自Ethan Hoover的Unsplash
高斯模糊
图 5. 应用高斯模糊的模糊参考图像(图 4)
高斯模糊是一种图像处理技术,用于减少图像噪声和模糊细节。这在面部模糊领域尤为有用,因为它擦除了图像中那部分的具体信息。它通过计算每个像素周围邻域的像素值的平均值来完成这一操作。这个平均值以被模糊的像素为中心,并使用高斯分布计算,使得附近的像素权重更大,而远离的像素权重更小。结果是图像变得更柔和,高频噪声和细节减少。应用高斯模糊的结果如图 5 所示。
高斯模糊有三个参数:
-
要进行模糊处理的图像部分。
-
内核大小:用于模糊操作的矩阵。较大的内核大小会导致更强的模糊效果。
-
标准差:较高的值会增强模糊效果。
f = image[y:y + h, x:x + w]
blurred_face = cv2.GaussianBlur(f, (99, 99), 15) # You can adjust blur parameters
image[y:y + h, x:x + w] = blurred_face
像素化
图 6. 应用像素化的模糊参考图像(图 4)
像素化是一种图像处理技术,其中图像中的像素被替换为较大的单一颜色块。这种效果通过将图像划分为网格单元来实现,每个单元对应一组像素。然后,将单元中所有像素的颜色或强度作为该单元中所有像素颜色的平均值,这个平均值应用于单元中的所有像素。这一过程创建了简化的外观,减少了图像中细节的层次。像素化的结果如图 6 所示。正如你所观察到的,像素化显著地使得识别个人身份变得复杂。
像素化有一个主要参数,决定了多少组像素应该代表一个特定区域。例如,如果我们有一个包含面部的(10,10)图像区域,它将被替换为一个 10x10 的像素组。较小的数字会导致更大的模糊效果。
f = image[y:y + h, x:x + w]
f = cv2.resize(f, (10, 10), interpolation=cv2.INTER_NEAREST)
image[y:y + h, x:x + w] = cv2.resize(f, (w, h), interpolation=cv2.INTER_NEAREST)
结果与讨论
我将从两个角度评估不同的算法:实时性能分析和特定图像场景。
实时性能
使用相同的参考图像(见图 4),测量每个面部检测算法定位图像中面部边界框所需的时间。结果基于每个算法的 10 次测量的平均值。模糊算法所需的时间微不足道,将不在评估过程中考虑。
图 4. 每个算法检测面部所需的平均时间(秒)
可以观察到,由于 YOLOv5 通过神经网络的单次处理实现了最佳性能(速度)。相比之下,像 MTCNN 这样的方法需要通过多个神经网络的顺序遍历,这进一步使得算法并行化的过程变得复杂。
场景基础性能
为了评估上述算法的性能,除了参考图像(见图 4)外,我选择了几张在不同场景下测试算法的图像:
-
参考图像(见图 4)
-
紧密聚集的人群 —— 评估算法捕捉不同面部大小的能力,一些面部较近,一些较远(见图 8)
-
侧视面部 —— 测试算法检测未直接面向摄像头的面部的能力(见图 10)
-
翻转面部,180 度 —— 测试算法检测旋转 180 度的面部的能力(见图 11)
-
翻转面部,90 度 —— 测试算法检测旋转 90 度的面部的能力(见图 12)
图 8. Nicholas Green 的人群照片,来自 Unsplash
图 9. 多个面部由 Naassom Azevedo 提供,来源于 Unsplash
图 10. 侧面视图面部由 Kraken Images 提供,来源于 Unsplash
图 11. 图 4 的面部翻转 180 度。
图 12. 图 4 的面部翻转 90 度。
Haar Cascade
Haar Cascade 算法通常在匿名化面部方面表现良好,尽管有一些例外。它成功地检测了参考图像(图 4)和‘多个面部’场景(图 9)。在‘人群’场景(图 8)中,虽然处理得不错,但有些面部未完全检测到或较远。Haar Cascade 在面对不直接对着相机的面部(图 10)和旋转面部(图 11 和 12)时遇到挑战,未能完全识别面部。
图 13. Haar Cascade 的结果
MTCNN
MTCNN 实现的结果与 Haar Cascade 非常相似,具有相同的优缺点。此外,MTCNN 在检测图 9 中肤色较深的面部时存在困难。
图 14. MTCNN 的结果
YOLOv5
YOLOv5 与 Haar Cascade 和 MTCNN 的结果略有不同。它成功地检测到其中一个未直接对着相机的面部(图 10)以及旋转 180 度的面部(图 11)。然而,在‘人群’图像(图 8)中,它未能像前述算法那样有效地检测到较远的面部。
图 15. YOLOv5 的结果
隐私
在处理图像隐私挑战时,重要的是要考虑如何在保持图像自然外观的同时使面部无法识别。
高斯模糊
高斯模糊有效地模糊了图像中的面部区域(如图 5 所示)。然而,它的成功依赖于用于模糊效果的高斯分布参数。在图 5 中,面部特征仍然可辨识,这表明需要更高的标准差和卷积核大小以获得最佳结果。
像素化
像素化(如图 6 所示)由于其作为面部模糊方法的熟悉感,通常在视觉上对人眼更为愉悦,与高斯模糊相比。像素化中使用的像素数量在此上下文中起着关键作用,因为较小的像素数量使面部不那么可识别,但可能导致外观不那么自然。
总体来看,相较于高斯模糊算法,像素化方法更受青睐。这主要由于其熟悉性和上下文的自然性,在隐私和美学之间取得了平衡。
逆向工程
随着 AI 工具的兴起,预测逆向工程技术可能会去除模糊图像中的隐私过滤器变得尤为重要。然而,模糊面部的行为不可逆地用更一般化的面部细节替代了特定的面部细节。目前,AI 工具只能在提供清晰的参考图像时逆向工程模糊的面部。这与逆向工程的需求本身相矛盾,因为它假设了对个体身份的了解。因此,面部模糊作为保护隐私的一种有效且必要的手段,面对不断发展的 AI 能力仍然是必不可少的。
在视频中的应用
由于视频本质上是图像的序列,因此修改每个算法以对视频进行匿名化相对简单。然而,在这里,处理时间变得至关重要。对于一个 30 秒的视频,录制帧率为 60 帧每秒(每秒图像),算法需要处理 1800 帧。在这种情况下,像 MTCNN 这样的算法将不可行,尽管它们在某些场景下有所改进。因此,我决定使用 YOLO 模型来实现视频匿名化。
import cv2
from yoloface import face_analysis
face=face_analysis()
def yolo_face_detection_video(video_path, output_path, pixelate):
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise ValueError("Could not open video file")
# Get video properties
fps = int(cap.get(cv2.CAP_PROP_FPS))
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# Define the codec and create a VideoWriter object for the output video
fourcc = cv2.VideoWriter_fourcc(*'H264')
out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height))
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
tm = time.time()
img, box, conf = face.face_detection(frame_arr=frame, frame_status=True, model='tiny')
print(pixelate)
for i in range(len(box)):
x, y, h, w = box[i]
if pixelate:
f = img[y:y + h, x:x + w]
f = cv2.resize(f, (10, 10), interpolation=cv2.INTER_NEAREST)
img[y:y + h, x:x + w] = cv2.resize(f, (w, h), interpolation=cv2.INTER_NEAREST)
else:
blurred_face = cv2.GaussianBlur(img[y:y + h, x:x + w], (99, 99), 30) # You can adjust blur parameters
img[y:y + h, x:x + w] = blurred_face
print(time.time() - tm)
out.write(img)
cap.release()
out.release()
cv2.destroyAllWindows()
网络应用
为了对不同算法进行简化评估,我创建了一个网络应用程序,用户可以上传任何图像或视频,选择人脸检测和模糊算法,处理后将结果返回给用户。该实现使用 Flask 与 Python 进行后端开发,利用上述库以及 OpenCV,前端则使用 React.js 与模型进行用户交互。完整代码可在这个链接找到。
结论
在本帖的范围内,探索、比较和分析了包括 Haar Cascade、MTCNN 和 YOLOv5 在内的各种人脸检测算法。该项目还关注了图像模糊技术。
Haar Cascade 在某些场景下证明是一种高效的方法,通常表现出良好的时间性能。MTCNN 作为一种在各种条件下具有强大人脸检测能力的算法脱颖而出,尽管它在面对不典型的面部姿态时表现不佳。YOLOv5 凭借其实时人脸检测能力,在时间是关键因素(例如视频)的场景中成为了一个出色的选择,尽管在群体设置中的准确性略有下降。
所有算法和技术都集成到一个单一的网络应用中。该应用程序提供了对所有人脸检测和模糊方法的简便访问和利用,并能够使用模糊技术处理视频。
这篇文章是我在斯科普里的计算机科学与工程学院“数字图像处理”课程工作的总结。感谢阅读!
解锁线性回归中交互项的力量
原文:
towardsdatascience.com/unlocking-the-power-of-interaction-terms-in-linear-regression-ba30c2cf158f
图片由 Denys Nevozhai 提供,来自 Unsplash
了解如何通过包含交互项来使你的线性模型更灵活
·发表于 Towards Data Science ·10 分钟阅读·2023 年 5 月 18 日
–
线性回归是一个强大的统计工具,用于建模因变量与一个或多个自变量(特征)之间的关系。在回归分析中,一个重要但常被忽视的概念是交互项。简而言之,交互项使我们能够检查目标与自变量之间的关系是否会根据另一个自变量的值而变化。
交互项是回归分析中的一个关键组成部分,了解它们如何工作可以帮助从业者更好地训练模型并解释数据。尽管它们很重要,但交互项可能很难理解。
在这篇文章中,我们将提供一个关于线性回归中交互项的直观解释。
回归模型中的交互项是什么?
首先,让我们考虑更简单的情况,即没有交互项的线性模型。这样的模型假设每个特征或预测变量对因变量(目标)的影响与模型中的其他预测变量无关。
以下方程描述了具有两个特征的模型规格:
为了使解释更容易理解,我们使用一个例子。假设我们对使用两个特征建模房地产价格(y)感兴趣:其大小(X1)和一个布尔标志,指示公寓是否位于市中心(X2)。β0 是截距,β1 和 β2 是线性模型的系数,ε 是误差项(模型未解释的部分)。
收集数据并估计线性回归模型后,我们得到以下系数:
了解估计的系数,并且 X2 是一个布尔特征后,我们可以根据 X2 的值写出两个可能的情景。
如何解释这些?虽然在房地产的背景下这可能没有太大意义,但我们可以说,市中心一平方米的公寓价格为 310(截距的值),每增加一平方米的空间价格增加 20。另一种情况下,唯一的区别是截距减少了 10 个单位。下图展示了两条最佳拟合线。
市中心和市外房地产的回归线
正如我们所见,这些线是平行的,并且它们具有相同的斜率——X1 的系数,在这两种情况下都是相同的。
交互项表示共同影响
此时你可能会争辩说,市中心一平方米的公寓比郊区的一平方米公寓更贵。换句话说,这两个特征可能对房地产价格有共同的影响。
因此,我们认为不仅截距在这两种情况之间应该不同,而且线的斜率也应该不同。如何实现这一点?这正是交互项发挥作用的时候。交互项使模型的规格更具灵活性,并允许我们考虑这些模式。
交互项实际上是我们认为对目标有共同影响的两个特征的乘积。以下方程展示了模型的新规格:
再次假设我们已经估计了我们的模型,并且我们知道了系数。为了简化,我们保留了与之前示例相同的值。请记住,在现实生活中,它们很可能会有所不同。
一旦我们写出 X2(市中心或非市中心)的两个情景,我们可以立即看到两条线的斜率(X1 的系数)不同。正如我们假设的那样,市中心的额外一平方米现在比郊区更贵。
解释带有交互项的系数
向模型中添加交互项会改变所有系数的解释。在没有交互项的情况下,我们将系数解释为预测变量对因变量的唯一影响。
在我们的案例中,我们可以说β1是公寓面积对其价格的唯一影响。然而,加入交互项后,公寓面积的影响在不同的X2值下是不同的。换句话说,公寓面积对其价格的唯一影响不再局限于β1。
为了更好地理解每个系数所代表的含义,让我们再看一眼包含交互项的线性模型的原始规范。提醒一下,X2是一个布尔特征,表示某个公寓是否在市中心。
现在,我们可以按以下方式解释每个系数:
-
β0 — 市中心以外公寓的截距(或具有布尔特征X2值为 0 的任何组),
-
β1 — 市中心以外公寓的斜率(价格的影响),
-
β2 — 两组之间截距的差异,
-
β3 — 市中心公寓与市中心以外公寓之间斜率的差异。
例如,假设我们正在检验一个假设,即公寓面积对其价格的影响是均等的,无论公寓是否在市中心。然后,我们将估计包含交互项的线性回归,并检查β3是否显著不同于 0。
关于交互项的一些附加说明:
-
我们展示了双向交互项,但也可以有更高阶的交互项(例如,涉及 3 个特征)。
-
在我们的示例中,我们展示了数值特征(公寓面积)与布尔特征(公寓是否在市中心)的交互。然而,我们也可以创建两个数值特征的交互项。例如,我们可以创建公寓面积与房间数量的交互项。有关更多细节,请参考References部分提到的来源。
-
交互项可能在统计上显著,但主要效应可能不显著。此时,我们应遵循层次原则,即如果我们在模型中包含了交互项,也应包括主要效应,即使它们的影响在统计上不显著。
Python 实操示例
经过所有的理论介绍后,让我们看看如何在 Python 中向线性回归模型添加交互项。和往常一样,我们从导入所需的库开始。
import numpy as np
import pandas as pd
import statsmodels.api as sm
import statsmodels.formula.api as smf
# plotting
import seaborn as sns
import matplotlib.pyplot as plt
# settings
plt.style.use("seaborn-v0_8")
sns.set_palette("colorblind")
plt.rcParams["figure.figsize"] = (16, 8)
%config InlineBackend.figure_format = 'retina'
在我们的例子中,我们将使用statsmodels
库来估计线性模型。对于数据集,我们将使用mtcars
数据集。我非常确定,如果你曾经使用过 R,你对这个数据集已经很熟悉了。首先,我们加载数据集:
mtcars = sm.datasets.get_rdataset("mtcars", "datasets", cache=True)
print(mtcars.__doc__)
执行代码片段会打印数据集的详细描述。我们只展示相关部分——总体描述和列定义:
====== ===============
mtcars R Documentation
====== ===============
The data was extracted from the 1974 *Motor Trend* US magazine, and
comprises fuel consumption and 10 aspects of automobile design and
performance for 32 automobiles (1973–74 models).
A data frame with 32 observations on 11 (numeric) variables.
===== ==== ========================================
[, 1] mpg Miles/(US) gallon
[, 2] cyl Number of cylinders
[, 3] disp Displacement (cu.in.)
[, 4] hp Gross horsepower
[, 5] drat Rear axle ratio
[, 6] wt Weight (1000 lbs)
[, 7] qsec 1/4 mile time
[, 8] vs Engine (0 = V-shaped, 1 = straight)
[, 9] am Transmission (0 = automatic, 1 = manual)
[,10] gear Number of forward gears
[,11] carb Number of carburetors
===== ==== ========================================
然后,我们从加载的对象中提取实际数据集:
df = mtcars.data
df.head()
mtcars 数据集的预览
以我们的例子为例,假设我们要调查每加仑英里数(mpg
)与两个特征的关系:重量(wt
,连续变量)和传输类型(am
,布尔值)。
首先,我们绘制数据以获取一些初步见解。
sns.lmplot(x="wt", y="mpg", hue="am", data=df, fit_reg=False)
plt.ylabel("Miles per Gallon")
plt.xlabel("Vehicle Weight");
每加仑英里数与车辆重量的散点图,按传输类型着色
仅通过目测图表,我们可以看出am
变量的两个类别的回归线会有很大不同。为了比较,我们首先使用没有交互项的模型。
model_1 = smf.ols(formula="mpg ~ wt + am", data=df).fit()
model_1.summary()
以下表格展示了在没有交互项的情况下拟合线性回归的结果。
从摘要表中,我们可以看到am
特征的系数在统计上不显著。利用我们已学到的系数解释方法,我们可以绘制am
特征两个类别的最佳拟合线。
X = np.linspace(1, 6, num=20)
sns.lmplot(x="wt", y="mpg", hue="am", data=df, fit_reg=False)
plt.title("Best fit lines for from the model without interactions")
plt.ylabel("Miles per Gallon")
plt.xlabel("Vehicle Weight")
plt.plot(X, 37.3216 - 5.3528 * X, "blue")
plt.plot(X, (37.3216 - 0.0236) - 5.3528 * X, "orange");
两种传输类型的最佳拟合线
由于am
特征的系数基本为零,这些线几乎重叠。
接下来,我们使用第二个模型,这次包括两个特征之间的交互项。下面展示了如何在statsmodels
公式中添加交互项作为额外输入。
model_2 = smf.ols(formula="mpg ~ wt + am + wt:am", data=df).fit()
model_2.summary()
以下摘要表展示了包含交互项的线性回归拟合结果。
我们可以从摘要表中快速得出两个结论:
-
所有系数,包括交互项,都在统计上显著。
-
通过检查 R2(以及它的调整变体,因为模型中的特征数量不同),我们可以说含有交互项的模型拟合效果更好。
与之前的情况类似,我们绘制最佳拟合线。
X = np.linspace(1, 6, num=20)
sns.lmplot(x="wt", y="mpg", hue="am", data=df, fit_reg=False)
plt.title("Best fit lines for from the model with interactions")
plt.ylabel("Miles per Gallon")
plt.xlabel("Vehicle Weight")
plt.plot(X, 31.4161 - 3.7859 * X, "blue")
plt.plot(X, (31.4161 + 14.8784) + (-3.7859 - 5.2984) * X, "orange");
两种传输类型的最佳拟合线,包括交互项
我们可以立即看到自动传输和手动传输汽车的拟合线(无论是截距还是斜率)之间的差异。
附加信息: 我们还可以使用scikit-learn
的PolynomialFeatures
添加交互项。这个变换器不仅提供了添加任意阶交互项的可能性,还创建了多项式特征(例如,现有特征的平方值)。更多信息,请参见文档。
总结
在处理线性回归中的交互项时,有几点需要记住:
-
交互项允许我们检查目标与特征之间的关系是否会根据另一个特征的值而变化。
-
我们将交互项添加为原始特征的乘积。通过将这些新变量添加到回归模型中,我们可以测量它们与目标之间交互的影响。正确解释交互项的系数对于理解关系的方向和强度至关重要。
-
使用交互项可以使线性模型的规格更加灵活(不同的线条具有不同的斜率),这可以导致更好的数据拟合和更好的预测性能。
你可以在我的GitHub 仓库中找到本文使用的代码。像往常一样,任何建设性的反馈都是非常欢迎的。你可以通过Twitter或在评论中联系我。
喜欢这篇文章?成为 Medium 会员,继续通过无障碍阅读来学习。如果你使用 这个链接 成为会员,你将以无额外费用支持我。提前感谢,期待与你相见!
你可能还对以下内容感兴趣:
详细参考常用的回归评估指标及其在各种实际应用中的应用…
了解如何正确解释线性回归的结果——包括变量变换的情况
[## 关于线性回归你可能不知道的一件事 ## 线性回归系数的解释
如何训练一个具有多个输出的单一模型
towardsdatascience.com ## 极简主义者的实验跟踪指南
入门实验跟踪的最低限度指南
towardsdatascience.com
参考文献
-
rinterested.github.io/statistics/lm_interactions_output_interpretation.html
-
janhove.github.io/analysis/2017/06/26/continuous-interactions
-
Henderson 和 Velleman (1981),互动式构建多重回归模型。生物统计学,37,391–411。
-
mtcars
数据集是 R 基础发行版的一部分,并且在 GNU 通用公共许可证 (GPL) 下发布
除非另有说明,否则所有图片均由作者提供。
最初发布于 NVIDIA 开发者博客 2023 年 4 月 26 日