如何去除熊猫身上不需要的部分
原文:https://towardsdatascience.com/pandas-remove-unwanted-parts-from-strings-92ac881e60a2
在 Pandas 中从列中删除不需要的子字符串
介绍
当使用 pandas 时,我们通常需要执行一些预处理任务,以便将数据转换成所需的形式。作为这一步骤的一部分,通常需要执行的一项常见任务涉及到字符串列的转换,我们会消除一些不需要的部分。
在今天的简短教程中,我们将讨论几种可能的方法,您最终可以应用于 pandas 数据帧,以便从某些列的字符串中删除任何不需要的部分。
首先,让我们创建一个示例 DataFrame,我们将在本文中引用它来演示一些概念,并展示如何在使用字符串列时获得预期的结果。
import pandas as pd df = pd.DataFrame(
[
(1, '+9A', 100),
(2, '-1A', 121),
(3, '5B', 312),
(4, '+1D', 567),
(5, '+1C', 123),
(6, '-2E', 101),
(7, '+3T', 231),
(8, '5A', 769),
(9, '+5B', 907),
(10, '-1A', 15),
],
columns=['colA', 'colB', 'colC']
) print(df)
***colA colB colC***
*0 1 +9A 100
1 2 -1A 121
2 3 5B 312
3 4 +1D 567
4 5 +1C 123
5 6 -2E 101
6 7 +3T 231
7 8 5A 769
8 9 +5B 907
9 10 -1A 15*
让我们假设我们想要转换存储在列colB
下的数据,以便移除前缀符号-/+
和后缀字母。
用熊猫。Series.str.replace()方法
[pandas.Series.str.replace()](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.str.replace.html)
方法可以用来替换Series
/ Index
中每一次出现的模式/正则表达式。
在我们的例子中,我们可以指定一个正则表达式将所有非数字值替换成一个空字符串。因此,下面的表达式可以解决这个问题:
df['colB'] = df['colB'].str.replace(r'\D', '')print(df)
***colA colB colC*** *0 1 9 100
1 2 1 121
2 3 5 312
3 4 1 567
4 5 1 123
5 6 2 101
6 7 3 231
7 8 5 769
8 9 5 907
9 10 1 15*
在使用正则表达式时,这无疑是众多选择之一。我猜你可以变得很有创造性,但是这个正则表达式应该是我们特定用例中最直接的。
另一个适合我们用例的选项是删除任何非整数字符:
df['colB'].str.replace(r'[^0-9]', '')
用熊猫。Series.str.extract()方法
在 pandas 中,从字符串中删除不需要的部分的另一个选择是[pandas.Series.str.extract()](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.str.extract.html)
方法,该方法用于提取 regex pat 中的捕获组作为 DataFrame 中的列。
在我们的例子中,我们将简单地提取字符串中我们希望保留的部分:
df['colB'] = df['colB'].str.extract(r'(\d+)', expand=False)print(df)
***colA colB colC*** *0 1 9 100
1 2 1 121
2 3 5 312
3 4 1 567
4 5 1 123
5 6 2 101
6 7 3 231
7 8 5 769
8 9 5 907
9 10 1 15*
用熊猫。Series.replace()方法
[pandas.Series.replace()](https://pandas.pydata.org/docs/reference/api/pandas.Series.replace.html)
是另一种选择,与我们在本教程中讨论的第一种方法非常相似。该方法将把to_replace
中给定的值替换为 value,并允许用户指定提供给to_replace
的值是否应该解释为正则表达式。
df['colB'] = df['colB'].replace(r'\D', r'', regex=True)
使用 map()方法
另一种选择是利用[map()](https://pandas.pydata.org/docs/reference/api/pandas.Series.map.html)
方法,该方法可用于根据输入映射或函数来映射熊猫Series
的值。
在我们的具体示例中,我们可以使用map()
来应用一个 lambda 函数,该函数从字符串的开头移除+/-
,从字符串的结尾移除任何 ascii 字符。
from string import ascii_lettersdf['colB'] = \
df['colB'].map(lambda x: x.lstrip('+-').rstrip(ascii_letters))print(df)
***colA colB colC*** *0 1 9 100
1 2 1 121
2 3 5 312
3 4 1 567
4 5 1 123
5 6 2 101
6 7 3 231
7 8 5 769
8 9 5 907
9 10 1 15*
同样,在处理这种类型的操作时,您可以变得非常有创造性,因此可以随意尝试最适合您的特定用例的方法。
你也可以在我最近的一篇文章中读到更多关于map()
、apply()
和applymap()
pandas 方法的内容。
使用列表理解
当处理大型熊猫数据帧时,您应该始终考虑矢量化,因为这将极大地提高执行操作的效率。
大多数时候,字符串函数可能很难向量化,因此它们可能以迭代的方式执行(这不是最好的方法)。
一个可能解决这个问题的好方法是列表理解。例如,代替str.replace()
,我们可以使用re.sub()
方法作为列表理解。
[*re.****sub****(pattern, repl, string, count=0, flags=0)*](https://docs.python.org/3/library/re.html#re.sub)
返回用替换
*repl*
替换*string*
中图案最左边不重叠出现的*string*
。如果没有找到*pattern*
,则不变地返回*string*
。*repl*
可以是字符串,也可以是函数;如果它是一个字符串,其中的任何反斜杠转义都会被处理。
import redf['colB'] = [re.sub('[^0-9]', '', x) for x in df['colB']]
或者,str.extract()
方法可以用re.search()
表示为列表理解:
import redf['colB'] = [re.search('[0-9]', x)[0] for x in df['colB']]
最后的想法
在今天的简短教程中,我们讨论了如何处理 pandas 数据帧,以便从包含 string 对象的列中截断任何不需要的部分。
请注意,在对相当大的数据帧应用这种转换时,应该考虑每种方法的性能。性能通常因数据帧的大小而异,例如,一种方法在应用于小数据帧时可能最有效,但在应用于大数据帧时就不那么有效了。
因此,尝试不同的方法并为您的特定用例应用最有效的方法总是有益的。
成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。
https://gmyrianthous.medium.com/membership
相关文章你可能也喜欢
Pandas Sidetable 简化了探索性数据分析过程
熊猫的实用附加产品
照片由维达尔·诺德里-马西森在 Unsplash 拍摄
Sidetable 是由 Chris Moffitt 创建的开源 pandas 实用程序库。它为 pandas 数据框架创建汇总表,这在探索性数据分析中非常有用。
sidetable 所做的也可以在熊猫身上实现,但是它简化了这个过程。您只需一行代码就可以获得数据的大致情况。
在撰写本文时,sidetable 有 5 个功能,并且正在改进中。因此,我们可以期待更多的即将到来。我们将为每个函数做一些示例,以理解它做什么以及它如何有助于有效的探索性数据分析。
您可以按如下方式安装 sidetable:
# terminal
$ python -m pip install -U sidetable# jupyter notebook
!pip install sidetable
安装后,您可以导入 sidetable 并将其用作 pandas 数据帧上的访问器。
import pandas as pd
import sidetable
我们将使用我用模拟数据创建的销售数据集。你可以从我的 GitHub 页面上的数据集库中下载。我们将在本文中使用的数据集称为商店销售数据。
sales = pd.read_csv(
"sales_data_with_stores.csv",
usecols = ["store", "product_group", "product_code", "cost",
"price", "last_week_sales"]
)sales.head()
(图片由作者提供)
类固醇的价值统计
假设我们想从产品组的角度获得一个概述,并寻找以下问题的答案:
- 存在多少个产品组?
- 就每个产品组包含的产品数量而言,每个产品组的规模有多大?
- 整个投资组合的累积覆盖率是多少?
我们可以通过使用 freq 函数的一行代码找到所有这些问题的答案。
sales.stb.freq(["product_group"])
(图片由作者提供)
有 6 个产品组,我们可以看到每个组包含多少产品。此外,结果表具有累积值。例如,PG4、PG5 和 PG6 占我们整个产品组合的 85%。
上表基于数据帧中的行数。比方说,我们希望看到销售的分布情况。这可以通过使用 freq 函数的 value 参数轻松完成。
sales.stb.freq(["product_group"], value="last_week_sales")
(图片由作者提供)
就产品数量而言,PG3 是第三大产品组,但其销售额最高。这对我们的分析是一个有价值的见解。
我们可能希望进一步分析,并分别查看每个商店产品组的表现。我们需要做的就是在 Python 列表中编写产品组和存储列。
sales.stb.freq(["product_group", "store"], value="last_week_sales")
一次 6 个问题
- 有多少观察值(即行数)?
- 有多少独特的价值?
- 最常值?
- 最频繁值有多少次观察?
- 最不频繁的值?
- 最不频繁值有多少个观察值?
使用 counts 函数,用一行代码就可以回答所有这些问题。
默认情况下,我们获得所有列的结果。但是,这些问题通常针对分类列。因此,如果我们只需要分类列的数据,我们可以使用 exclude 参数排除数值列。
sales.stb.counts(exclude="number")
(图片由作者提供)
让我们对 store 列做一个快速的结果检查:
sales["store"].value_counts()**# output**
Daisy 470
Violet 330
Rose 200
Name: store, dtype: int64
最常见的值是 daisy,它有 470 个观察值,这就是上面的 sidetable 所示。
缺少值
用熊猫寻找缺失的价值观并不是一件复杂的事情。isna 和 sum 函数可以一起使用来查找每列中缺失值的数量。sidetable 增加的是丢失值的百分比。
sales.stb.missing()
(图片由作者提供)
缺失函数提供了数据帧中缺失值的快速概览。
如果只需要包含缺失值的列,请将 clip_0 参数的值设置为 True。
汇总值的小计
Sidetable 使得向 groupby 函数的结果添加小计变得非常简单。对于某些任务,小计可能会派上用场。让我们做有和没有小计的例子来看看区别。
sales_filtered = sales[sales["product_group"].isin(["PG1", "PG2"])]sales_filtered.groupby(["store", "product_group"]).agg(
total_sales = ("last_week_sales", "sum")
)
不带小计(图片由作者提供)
sales_filtered.groupby(["store", "product_group"]).agg(
total_sales = ("last_week_sales", "sum")
).stb.subtotal()
带小计(图片由作者提供)
需要注意的是,如果在 groupby 函数的输出中将分组显示为列(即,将 as_index 参数的值设置为 False),则不会计算小计。
用多级列索引展平数据帧
假设给我们一个数据帧,如下所示:
(图片由作者提供)
让我们检查列名:
df.columns**# output**
MultiIndex([('total_sales', 'PG1'),
('total_sales', 'PG2')],
names=[None, 'product_group'])
它是一个多索引,每个列名是一个元组。我们可以通过使用 sidetable 的展平功能来展平列名。
df.stb.flatten()
(图片由作者提供)
它看起来更好,更容易工作。
Sidetable 是探索性数据分析中非常实用的工具。我们在本文中所做的操作并不复杂,但是简化它们并没有坏处。Sidetable 允许用一行代码执行所有这些操作。
你可以成为 媒介会员 解锁我的全部写作权限,外加其余媒介。如果你已经是了,别忘了订阅https://sonery.medium.com/subscribe如果你想在我发表新文章时收到电子邮件。
感谢您的阅读。如果您有任何反馈,请告诉我。
用于时间序列分析的熊猫把戏
原文:https://towardsdatascience.com/pandas-tricks-for-time-series-analysis-726618532172
用于分析熊猫时间序列的 3 个函数(带代码)
熊猫几乎不需要介绍。对于那些刚刚开始接触数据科学领域的人来说,Pandas 代表 Panel Data Analysis,是目前 Python 中数据转换的主要库。
除了很多其他的东西,我们还可以使用这个漂亮的包来分析时间序列。可以,有很多方法可以用。在这篇文章中,我们将会看到其中的一些。
时间序列
让我们首先商定什么是时间序列。
在连续时间内获得的一个量的一系列值,它们之间通常有相等的间隔。(牛津语言)
简单地说,时间序列是指我们在一段时间内测量的东西,比如说商店的销售额。因此,我们将从 1 月、2 月、3 月开始取值,以此类推,直到 12 月。这将为我们提供一个包含月份和销售额的数据集。这是一个时间序列。
熊猫创造的时间序列。图片由作者提供。
熊猫对处理时间序列有充分的准备。下一节将展示一些有助于我们分析的代码片段。
使用熊猫
咱们import numpy as np
和import pandas as pd
吧。
我们应该学习的第一件事是如何在熊猫中创建一组日期。这在生成用于训练目的的时间序列时非常有用。
因此,如果我们使用date_range()
,就有可能创建我们想要的任意多的日期,使用任何需要的时间间隔。
# Create a date range of 7 days
pd.date_range("2022-01-01", periods=7, freq='D')**[OUT]:** DatetimeIndex(['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04','2022-01-05', '2022-01-06', '2022-01-07'], dtype='datetime64[ns]', freq='D')
注意,输出是一个从我们的初始日期起 7 天的列表。还有许多其他频率可用作freq
。最常见的是表示每周的"W”
、表示月末日期的"M”
、表示月初的"MS”
以及其他许多可以在这里找到的。
知道了这一点,我们可以继续创建一个数据集,用于本帖中的练习。我们将设置一个种子,这样您就可以重现与我相同的结果,然后我们创建一个包含每日日期和销售额的数据集,并将日期设置为索引。
# Setting a seed for reproductionability
np.random.seed(12)# Create dataset
df = pd.DataFrame({
'date': pd.date_range("2022-01-01", periods=180, freq='D'),
'sales': np.random.randint(1000, 10000, size=180)}) # Set index
df = df.set_index('date')
数据集头。图片由作者提供。
现在,我们将继续熊猫的功能代码。请注意,时间序列的一个重要特征是将日期作为索引。当我们在前面的代码中设置索引时,我们已经满足了这个要求。
重新取样
第一个有趣的函数是resample
。它所做的是接受一个数据集或一个系列(一列),并根据作为参数提供的rule
聚集数据。这是一种按日期按功能分组的方式。
我们的数据集日期频率是每天,对吗?如果要转化为月销售额呢?
我们可以用resample
做到这一点,但是由于数据被聚合,我们还必须添加一个函数来处理聚合的数字。我们可以sum()
或计算mean()
或median()
,看月份的max()
等。
# Resample by month end date
df.resample(rule= 'M').mean()
按月取平均值,并将指数设置为每月结束日期,结果如下。
按月平均。图片由作者提供。
现在让我们按周销售额来绘制汇总数据。
# Resample plot
df.resample('W').mean().plot(figsize=(15,5), title='Avg Weekly Sales');
平均每周销售额。图片由作者提供。
看起来我们的销售额在三月和四月之间有所下降,在六月中旬达到顶峰。
变化
Pandas 中的shift
功能是让你在一列中上下移动数据。假设我们要将当天的销售额与前一天的销售额()以及当天的销售额与下周的销售额()进行比较。下面是如何使用shift
完成的。
# Create a copy of the data
df_shift = df.copy()#Shift one day up
df_shift['next_day_sales'] = df_shift.sales.shift(-1)#Shift one week up
df_shift['next_week_sales'] = df_shift.sales.shift(-7)
看看我们如何拉动第二天的销售额和 7 天的销售额,将它们放在一起进行比较。
当天对次日对下周。图片由作者提供。
显然,如果需要,我们可以在这些列之间创建差异。
# Net gain/loss week over week
df_shift['one_week_net'] = df_shift.sales - df_shift.sales.shift(-7)
滚动平均值
下一个函数是从事股票交易的人常用的工具,即rolling
平均值。事实上,这个功能是可以与其他功能结合使用的,不仅意味着,而且它恰好是最常见的。
这个函数创建一个窗口来聚集数据。因此,举例来说,如果我们创建 2 天的移动平均值,rolling
将从数据集中提取连续 2 天的子集,并计算聚合结果,即最大值、中值或最常用的平均值。
# Calculate 2 days average with Pandas
df.rolling(2).mean()
在下图中,我们可以看到第一个值是一个NaN
,因为没有足够的数据(本例中为 2 个数据点)来创建平均值。因此,对于第二个点,它获取原始数据集的前两行,并计算平均点。(6787 + 4325)/2 = 5556.
2 天移动平均线。图片由作者提供。
滚动平均非常常用于绘制数据,是趋势的一个很好的指示。子集周期越长,线越平滑。注意,100 天滚动平均线几乎是一条线,只是显示主要趋势,而 7 天平均线随着数据上下波动。
# Rolling plot
df.sales.plot(figsize=(25,8), legend=True, linestyle='--', color='darkgray')df.rolling(window=7).sales.mean().plot(legend=True, label='7 day average', linewidth=2)df.rolling(30).sales.mean().plot(legend=True, label='30 day average', linewidth=3)df.rolling(100).sales.mean().plot(legend=True, label='100 day average', linewidth=4);
实际数据(灰色)与 7、30 和 100 天平均值的比较。图片由作者提供。
在你走之前
我相信熊猫对我们分析任何数据都是有价值的,当然也包括时间序列。这篇文章中介绍的这三个函数可以帮助你开始时间序列分析。好好利用他们。
resample
:将数据从日频率转换到其他时间频率。shift
:在列中上下移动数据,进行比较或计算。rolling
:创建滚动平均线,观察趋势。
如果你喜欢这些数据,请关注我。
http://gustavorsantos.medium.com/
如果你想成为中等会员,这里有一个介绍链接。
参考
熊猫日期范围:https://pandas . pydata . org/docs/reference/API/pandas . Date _ Range . html
熊猫再取样:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.resample.html
熊猫转移:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.shift.html
熊猫滚滚:https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.rolling.html
Pandas vs Dask vs Datatable:处理 CSV 文件的性能比较
熊猫可能不再是最好的选择了
马丁·雷施在 Unsplash 上的照片
W 说到处理 CSV 文件,每个人脑海中出现的第一个工具就是熊猫。毫无疑问,pandas 是一个伟大的框架,dataframe 提供了一种极其精简的数据表示形式,帮助我们更好地分析和理解数据。
最近做了一个任务,要求我合并 30K+ CSV 文件。我的神感是用熊猫,但是因为某些文件操作的表现不太顺利。在这篇文章中,我想和你分享另外两种选择,并比较它们和熊猫的行为和表现。最后,您将理解每个库的权衡,并能够做出正确的选择。
我对实验得出的一些结果感到非常惊讶,因为除非你实际尝试,否则无法预测。希望这篇文章可以节省你试错的时间,帮助你在选择库的时候做出更好的决定。
Dask
大多数数据分析 Python 库(如 Numpy、pandas 和 scikit-learn)的一个问题是,它们不能扩展到单台机器之外。Dask 是一个开源库,当您处理大型数据时,它为分析提供了高级并行化。它可以在需要时将这些分析包扩展到多核机器和分布式集群。它提供了一个与 pandas 相似的 API 接口,以确保一致性并最小化摩擦。
数据表
Datatable 是另一个考虑到性能的 Python 库。与 dask 不同,datatable 的目标是在一台单节点机器上以尽可能快的速度执行大型数据处理。同时,它与 pandas 的互操作性提供了轻松转换到另一个数据处理框架的能力。
这两个库都旨在提高 pandas 的性能,并保持与 pandas 相似的界面以方便使用。在接下来的几节中,我在我的 Macbook Pro(2.6 GHz 6 核英特尔酷睿 i7,16GB 内存)上进行了实验,在不同的环境中运行时,您可能会得到不同的结果。
读取单个 CSV 文件
让我们从最简单的操作开始——读取单个 CSV 文件。让我惊讶的是,在最基本的操作上,我们已经可以看到巨大的差异了。数据表比熊猫快 70%,而 dask 快 500%!结果是各种各样的数据帧对象,它们有非常相同的接口。
读取多个 CSV 文件
当我试图用熊猫来完成这个任务时,这就是我被卡住的地方。pandas 没有提供可以在一行中读取多个 CSV 文件的接口。唯一的方法是做一个 for 循环,将每个数据帧追加到一个列表中,最后,使用pd.concat
来组合所有这些数据帧。但这是相当低效的。让我们检查以下内容:
这个结果也很有趣,因为事实证明,在读取多个 CSV 文件方面,datatable 的性能比 pandas 差,这与我的预期相反。尽管如此,达斯克仍然赢得了比赛,表现比熊猫好 4 倍。
警告— CSV 文件有不同的格式
组合多个 CSV 文件时,由于版本不同或数据文件损坏,CSV 文件可能会有不同的格式。在这种情况下,我们应该小心不要无意中混淆了不同的 CSV 文件。
新列
在这个例子中,我准备了两个文件,其中一个有一个额外的列。我试图模拟一个真实的场景,在这个场景中,源文件中有一个微小的模式变化。
这是你所期望的吗?
Pandas 将使用新模式作为目标模式,新列将被回填为旧数据中的 **NaN**
。列表files
中的顺序无关紧要,因为 pandas 将选择具有更多列的模式,而不管读取顺序如何。
另一方面,Dask 让我很惊讶。它使用列表中第一个文件的**模式作为目标模式,并忽略不匹配的文件。**当我反转files
时,我得到了完全不同的结果,因为先读取了一个不同的文件。这有点冒险,因为它可能会在不通知您的情况下忽略许多行。一种解决方法是读取每个文件的文件头,并在合并之前进行比较。尽管这产生了一点点开销,但总的处理时间仍然比 pandas 快,因为在“合并”阶段获得了巨大的性能增益。
与其他的相比,数据表是相当安全的。如果在模式中发现差异,它将引发异常。你可以在rbind
函数中添加force=True
,这样它就会有和熊猫一样的行为。
重命名现有列
另一个常见的模式更改是重命名现有的列,这是一个突破性的更改。在这个例子中,data.csv
只包含gross_amount
,而data_rename_col.csv
只包含net_amount
。看到前面的例子后,在检查结果之前,让我们猜一猜。
所以,熊猫的结果既包含了gross_amount
又包含了net_amount
,它用NaN
填充了缺失的值。Dask 给出与之前相同的结果,这取决于它首先读取的文件。在这种情况下,Datatable 抛出一个不同的异常,其名称为中断列。这在调试过程中很有帮助。一般来说,它们都继承了上一个例子的相同行为。
计算聚合
一项重要的分析是计算聚合,如最小值、最大值、总和、平均值和中值,其中单个数字可以洞察整个(部分)数据集。那么这些计算的性能如何呢?
根据 dask 文档:一般来说,dask . data frame group-by-aggregations 的性能与 pandas group-by-aggregations 大致相同,只是可伸缩性更强。
计算聚合的性能也是一样的。但与熊猫相比,dask 能够在集群中扩展解决方案。
写入 CSV 文件
最后一部分是卸载。所有这 3 个库都有相同的接口.to_csv()
将数据帧保存到 CSV 中。除此之外,dask 还支持 Apache Parquet 格式,这是一种流行的柱状二进制格式,旨在实现高效的数据存储和检索。它提供了高效的数据压缩和编码方案,增强了批量处理复杂数据的性能。
如你所见,获胜者是镶木地板格式的 dask。Pandas 和 datatable 的表现一样。常规 CSV 格式的 Dask 性能最差,这与读取 CSV 文件的性能完全相反。parquet 的高性能是因为数据被分成了几个分区。默认情况下,dask 会将每个 parquet 文件作为数据帧中的一个分区单独加载,这样更便于并行加载。
此外,dask 在默认情况下用 snappy compression 写出 parquet 文件。快速压缩通常是分布式计算中文件的最佳选择。虽然它压缩文件的力度不如 gzip 等其他压缩算法,但在解压文件时速度更快。您也可以用其他压缩算法覆盖它。
结论
我希望这篇文章能够让您对 pandas、dask 和 datatable 的不同方面有一个整体的了解。事实证明,没有一个图书馆是完美的。Dask 擅长读写文件,尤其是使用它的 parquet 格式。它能够将您的解决方案分发到一个集群。Datatable 试图以稍好的性能模仿熊猫的行为。Pandas 是另外两个库的核心,提供了最完整的计算方法。
比较不同的工具并了解它们的优缺点总是很有趣的。我鼓励你在你感兴趣的领域也做这样的练习,并与社区分享。它将使许多人受益。
让我知道你对这三个图书馆的看法!干杯!
参考
熊猫大战 SQL——第一部分:美食广场和米其林风格的餐厅
(左)杜尚·哈努斯卡在 flickr 上的照片 | ( 右)普拉依特诺在 openverse 上的照片
tldr: 这是一系列对比 Pandas 和 SQL(数据框架和数据库)的博客文章中的第一篇。事实上,数据框架和数据库都是旧思想,可以追溯到几十年前。数据库选择可伸缩性、健壮性和效率,而 Pandas 选择简洁、灵活和方便。我们能指望两个世界都好吗?是啊!
每隔几个月,就会有一条关于熊猫大战 SQL 的推文,比如 2020 年的这条:
我不得不给出一个结合了 Pandas 和 SQL 的代码库给那些对 Pandas 或 SQL 一无所知的学术界人士,但是(1)他们可能知道一些 R,(2)他们可能有学术界的朋友/同行知道 Pandas 或 SQL。当一个任务可以在 Pandas 或 SQL 中完成时,应该使用哪一个?
-高级 PowerPoint 工程师(@ ryx commar)2020 年 7 月 19 日
或者这个:
SQL 比 python/r 更受当今数据人员的欢迎,是因为它更好,还是因为人们可以使用它而无需设置开发环境,将数据从一个地方移动到另一个地方,或者做任何其他困难的“搜索另一个神秘的终端错误”的事情?
-本斯坦西尔(@本斯坦西尔)2022 年 1 月 25 日
然后这个月早些时候的这个:
跟进问题:你什么时候从 SQL 切换到另一个 PL (Python,JS 等)。)?硬的时候还是不符合人体工程学的时候还是别的?
-莎拉·卡坦扎罗(@ Sarah cat 21)2022 年 6 月 15 日
所有这些推文本质上都问了同一个问题:就数据工作而言,Pandas/Python 能比 SQL/数据库更好吗 — 如果能,什么时候?显然,使用熊猫的数百万数据科学家在他们首选的工具上不可能都是错的——但是很难找到一种资源以一种确定的方式回答这个问题。
所以我们决定写一系列的博客文章来探讨这个问题,比较熊猫和 SQL/数据库。这是第一篇,我们将在未来几周发布后续文章。即使你没有阅读任何帖子,其中的要点是:数据框架和数据库已经以某种形式存在了几十年。两者都在数据分析和数据科学领域占有重要地位!
无论如何,说到这篇博文的主题。嗯…谁不喜欢美食类比呢?让我们开始吃吧。
首先,让我们谈谈关系数据库——一项成熟的、有几十年历史的技术,市值高达数千亿美元,由 Snowflake、Databricks、Oracle、Amazon、Microsoft 和 Google 等公司提供。我们都喜欢关系数据库。
数据库允许你用一个预定义的结构或者模式来表示直观集合中的数据,这些集合被称为关系。数据库让你使用 SQL 或“结构化查询语言”来操作关系,这种语言是 IBM 在 20 世纪 70 年代发明的,通常被称为“星系间数据峰”SQL 和关系数据库经受住了时间的考验,尽管许多人试图取代它们,包括 2000 年代中期的 noSQL 运动。数据库将比我们所有人都长寿。
一家高级餐厅。我还没去过一个用餐巾纸做天鹅的地方。也许有一天。(图片由杰伊·温宁顿在 unsplash 上拍摄)
事实证明,关系数据库非常像米其林星级高级餐厅。这种类型的餐馆非常正式:你需要穿衬衫或连衣裙,而不是短裤或 t 恤——就像你的数据库一样,在你操作数据之前需要一个正式的模式。菜单上没有太多的选项:它通常是一个固定的菜单,有一组精心挑选的小选项,就像您的数据库一样,它只支持 SQL 中的少量关键字(SELECT-FROM-WHERE-GROUP-BY……)。但就其作用而言,高级餐厅——就像数据库一样——是经过微调的、高效的和有效的。
或者,让我们考虑一个非常不同的野兽, Pandas ,数据科学的流行数据框架库。
Pandas 是一个快速、强大、灵活且易于使用的开源数据分析和操作工具。Pandas 的起源源于统计社区,在 2008 年移植到 Python 之前,该社区在 20 世纪 90 年代的 [S](https://en.wikipedia.org/wiki/S_(programming_language) 和 21 世纪的 [R](https://en.wikipedia.org/wiki/R_(programming_language) 中开发了底层数据帧抽象。因此,数据框架已经以某种形式 — 存在了三十多年,这种形式是为了统计数据分析和数据科学 — 的目的而明确设计的!
Pandas 在数据科学家和数据分析师中非常受欢迎,每周被下载 3000 万次。事实上,Python 现在是世界上发展最快的语言,这在很大程度上要归功于熊猫。在各种文章中,它被称为数据科学中最重要或最受欢迎的工具。
那么熊猫的数据框架和数据库相比如何呢?Pandas dataframe 系统类似于数据库,但也有所不同。与数据库关系不同,在数据帧中,Pandas 允许在一列中使用混合类型的数据,并维护顺序概念。除了列标签之外,数据框架还支持行标签,这使得引用数据变得很容易。pandas 支持 600 多种功能,涵盖数据清理、准备、转换和汇总,让您可以对数据做几乎任何可以想象的事情,从关系、矩阵(线性代数)和电子表格中提取数据,涵盖数据清理、转换、准备、特征化和探索。
Dataframe 数据模型和 600 多个函数(图片由作者提供)
因此,如果关系数据库就像米其林星级餐厅,那么熊猫就非常像一个美食广场。美食街不在乎你穿什么——就像熊猫一样,它不需要你在加载数据前整理或清理数据。你可以零零碎碎地订购你喜欢的东西——就像熊猫一样,它让你可以逐步提出你的查询。你甚至可以做一些不虔诚的事情——比如混合寿司和薯条。你甚至可以在薯条上放冰淇淋。你可能会得到一些奇怪的眼神,但没有人能阻止你。从最老的人到最年轻的人,每个人都是快乐的!
典型的美食广场(由保罗·芬威克在 openverse 上拍摄)
让我们将我们的数据库美食体验与熊猫美食广场进行对比。就方便性而言,关系数据库要求您一次订购整个 SELECT-FROM-WHERE 查询;Pandas 允许你一次下一个订单,检查它,然后如果你愿意的话可以订购更多。我们的美食数据库要求在你进门之前有一个正式的模式(如果你穿短裤,你会被踢出去的!);Pandas food court 并不在乎——在一个列中混合类型完全没问题,这在数据清理的早期阶段很方便。Pandas 也非常平等:它对行和列一视同仁,对数据和元数据一视同仁;祝你好运,从你的美食数据库里得到那种待遇!我们的美食数据库菜单上只有几样东西——你的“从哪里选择”查询;熊猫拥有一切可以想象的东西,从寿司到汉堡到冰淇淋,有数据科学家贡献的 600+开源函数。
那么坏处是什么呢?不幸的是,尽管 Pandas API 有许多优点,但 Pandas 实现并没有真正扩展,因为它是在没有任何正式基础的情况下以特别的方式发展的。它只支持单线程执行,所以即使在一个强大的集群上运行它也不会给你带来任何好处。由于它完全在内存中操作,并制作许多副本,因此经常会引发内存不足(OOM)错误。最后,在一个序列中没有跨多个 pandas 操作符的真正的查询优化。所以你经常发生的事情是:你对你的数据进行采样,用熊猫进行探索,然后重写你的工作流程来大规模运作。
不幸的是,这太普通了(图片由作者提供,使用 imgflip.com)
因此,如果我们对比 Pandas 与数据库等大数据框架,这些大数据框架在性能谱上得分很高,跨越了可扩展性、健壮性和效率。正如我们将在随后的博客文章中探讨的那样,Pandas 在易用性方面得分很高,包括简洁、灵活和方便。
比较熊猫和大数据框架(图片由作者提供)
一个悬而未决的问题是我们是否能两者兼得:我们能保留 Pandas API 但改变实现以获得可伸缩性、健壮性和效率吗?有一些团队正在研究这个问题——例如,通过将类似数据库的技术应用于熊猫——包括一个开源项目 Modin ,它旨在成为熊猫的可扩展替代者。
在我们的 Pandas vs. SQL 系列的下一篇文章(第 2 篇,共 4 篇)中,我们认为 Pandas 是更简洁的语言。更多阅读 此处 !
如果你是熊猫或者 SQL 爱好者(或者两者都是!)我们希望收到您的来信。欢迎在 Twitter 上关注我们,了解更多类似的内容,并回复我们的帖子!
原载于 2022 年 6 月 28 日https://pounder . io。
Pandas 与 SQL —第 3 部分:Pandas 更灵活
原文:https://towardsdatascience.com/pandas-vs-sql-part-3-pandas-is-more-flexible-cb43167189f5
Pandas 灵活的数据模型非常适合数据科学用例
美洲虎坦巴克在 Openverse 拍摄的照片(CC BY-ND 2.0)
TL;在这篇文章中,我们在三个坐标轴的第二个坐标轴上比较了 Pandas 和 SQL:灵活性。我们介绍了 Pandas dataframe 数据模型提供额外灵活性的八种方式,这使其非常适合数据科学和机器学习,优于 SQL 的关系模型。
我们继续寻求理解 Pandas 和 SQL 的相对优势——两种用于数据操作和分析的流行且强大的语言。这是比较熊猫和 SQL 的系列帖子中的第三篇;下面是之前的两个帖子:熊猫大战 SQL —第一部分:美食广场和米其林风格的餐厅和熊猫大战 SQL —第二部分:熊猫更简洁。
正如我们在上一篇关于 Pandas 与 SQL 的文章中所看到的,Pandas 有 600 多个函数,可以让您以各种强大的方式对数据进行操作,这些方式在 SQL 中是不可能或极难做到的,涵盖了一系列关键的机器学习、线性代数、特征化和数据清理操作。在这篇文章中,我们展示了 Pandas dataframe 数据模型(或抽象)如何提供额外的灵活性,使其非常适合数据科学和机器学习,优于 SQL 底层的关系模型。因此,前一篇文章关注的是数据框架代数,而这篇文章将关注数据框架数据模型。
以下是 Pandas 数据框架比关系/SQL 数据框架更灵活的多种方式的列表:
- 将数据转移到元数据,然后再转移回来
- 行和列元数据
- 混合型色谱柱
- 灵活的模式
- 元数据转换
- 柱状操作
- 分层元数据
- 对元数据差异的容忍
我们将从一个简单的数据集开始,类似于从电子表格加载数据后可能得到的数据集。这是一个包含 2020 年夏季奥运会最佳表现国家的奖牌总数的数据集。
1.在 Pandas 中,你可以在元数据之间来回移动数据!在 SQL 中,您不能。
关于上面的数据集,您可能注意到的第一件事是,我们有像A, B, C, ...
这样的非描述性列名,而实际的列名是数据的一部分(第 0 行)。理想情况下,我们希望用这些列名替换A, B, C, ...
。我们可以通过以下方式做到这一点:
我们所做的只是将标题行提取到header
,修整数据帧以保留最后的n-1
行,并通过df.columns
设置列名。
类似地,可以将信息从元数据(列标题)反向移动到数据。见下文。
我们在这里所做的是通过to_frame()
将列名提取为数据帧,通过单个字母T
将数据帧转置为一行,最后通过pd.concat
将该行与旧数据帧垂直连接。
在 SQL 中,实际上不可能将数据移动到元数据(列名)中,或者将数据移动回来。
2.Pandas 支持行和列元数据;SQL 只有列元数据。
虽然 Pandas 像数据库一样支持列元数据(即列标签),但 Pandas 也支持行标签形式的逐行元数据。如果我们想以直观的方式组织和引用数据,这是很方便的。这里有一个例子:我们从重新加载数据帧开始。
现在我们需要做的就是使用set_index
来设置行标签或索引。这里我们将其设置为数据中的Name
列。
就像列标签一样,我们也可以使用reset_index
命令将行标签移回到数据中。
数据库没有行标签的概念。您可能声称行标签功能可以用一个*PRIMARY* KEY / *UNIQUE*
关键字来复制,但这并不完全正确——行标签实际上不需要唯一。这是熊猫灵活的另一种方式!
3.熊猫允许混合式栏目;SQL 只支持单一类型的列
由于它在数据科学中的使用,在不同的清理阶段对数据进行操作,Pandas 没有对每列强制严格的类型。再看我们的例子:
这里,Ranking
列包含字符串和整数。如果我们检查该列的类型,我们将看到以下内容:
我们所做的是在通过apply
推断出列中每个值的类型之后,通过value_counts
计算每个类型的值。正如您在输出中看到的,Ranking
列包含四个整数和一个字符串(—)。如果我们希望将字符串值强制转换为空值,以便该列具有单一类型,我们可以通过以下方式实现:
4.在 Pandas 中,输出模式可以根据数据灵活变化;在 SQL 中,模式是固定的
在 SQL 中,输出的模式(即一组列及其类型)基于输入和查询的模式进行约束。Pandas 没有这种限制,允许输出根据数据而变化,从而提供了额外的灵活性。这里有一个例子,也是在我们熟悉的数据集上:
如您所见,get_dummies
函数为Continent
列中的每个值创建了一个新的布尔列。这对机器学习非常有帮助,因为大多数机器学习包只接受数字数据,所以所有的字符串列都需要转换成数字列。如果我们从数据集中省略了对应于ROC
的最后一行,就不会有对应于Continent_Europe/Asia
的列。通过这种方式,输出列可以依赖于数据——非常简单!
5.Pandas 允许您灵活地转换元数据(列/行标签);在 SQL 中,您不能
Pandas 认识到模式总是不断变化的,并赋予我们灵活清理元数据的超能力,而不仅仅是以简单的编程方式清理数据。这里,如果我们发现Continent_
前缀很难跨列重复,我们可以使用一个命令在所有列中删除它:
噗,就这样,没了!不幸的是,SQL 不能像 Pandas 那样给你操作列名的能力。您需要手动指定每个列名将如何更改。
6.Pandas 允许您像操作行一样操作列;SQL 没有
不同于 SQL 对行和列的不同处理,Pandas 对它们的处理是一样的。从上面使用Name
列作为行索引/标签的df_name_index
开始,我们可以简单地转置它,使行成为列,列成为行,如下所示:
最酷的是,Pandas 自动推断新列的类型,而无需用户做任何工作。在这种情况下,所有这些列都是混合型的,因为它们既有整数也有字符串。以这种方式组织数据通常更容易比较。除了转置,我们还可以像对待行一样沿着列应用大多数函数。我们在之前的博客中已经提到了这方面的例子。
7.Pandas 让你拥有层次化的元数据;SQL 没有
分层元数据帮助我们以更易于阅读和直观的方式组织和呈现信息。幸运的是,Pandas 支持分层的行标签和列标签。假设我们想要创建分层的行标签:
正如我们在上面看到的,我们通过set_index
操作符创建了一个由一对大陆和国家名称组成的行标签层次结构。通过使用T
操作符转置这个结果,我们得到了一个分层列标签的例子:
在上面的结果中,列标签的第一行捕获了大陆,而第二行捕获了国家名称。与 Pandas 不同,SQL 和关系数据库不支持分层元数据。对此最好的代理是使用半结构化数据格式(例如 JSON、XML),关系数据库通常支持这种格式作为列中的特殊数据类型。但是,在这些列上实施分层模式是很困难的,对这些嵌套数据格式的操作也是如此。
8.Pandas 在对具有不同元数据的多个数据帧进行操作时是宽容的;SQL 不是
当在多个数据帧上操作时,Pandas 操作允许模式差异。我们将通过考虑一个常见的二元运算concat
来说明这一点。假设我们在当前数据框架旁边有另一个数据框架,包含关于另外三个国家(澳大利亚、法国和荷兰)的信息。我们可以使用concat
操作符来执行具有匹配模式的两个数据帧的有序联合,如下所示:
得到的数据帧有八行。这是一个简单的例子,类似于 SQL *UNION*
操作符。现在假设两个数据帧的模式不同;具体来说,df2
没有金牌榜栏目。这是我们从熊猫身上得到的:
正如我们所看到的,Pandas 采用了一种“尽力而为”的方法,继续执行有序的联合(即连接),为两个数据帧之一中缺少的列填充NaN
(即 null)值。在这里,SQL 会给出一个错误,因为模式需要匹配一个联合。
结论
正如我们在上面的例子中看到的,Pandas 中的 dataframe 抽象比 SQL 下的关系模型灵活得多。从最终用户的角度来看,这种额外的灵活性通常使表示和使用数据更加直观。此外,在执行数据清理时,这种增加的灵活性是必不可少的:数据通常是脏的,具有异构类型和不精确、未指定和/或未对齐的模式。最后,Pandas 数据模型放弃了任意的区分:行和列是等价的,数据和元数据是等价的——这使得表达许多重要的数据科学和机器学习操作变得更加方便。
如果你能想到 Pandas 比 SQL 更灵活的其他例子,或者相反,我们很乐意听到!欢迎在推特上关注我们,了解更多类似的内容,并在这里回复我们的推特!
在我们的 Pandas 与 SQL 系列的下一篇文章(第 4 篇,共 4 篇)中,我们认为 Pandas 是更方便的语言。在这里阅读更多!
原载于 2022 年 7 月 26 日https://pounder . io。
Pandas 与 SQL —第 4 部分:Pandas 更方便
原文:https://towardsdatascience.com/pandas-vs-sql-part-4-pandas-is-more-convenient-8e9744e2cd10
gamene 在 Openverse 的照片(CC BY 2.0)
TL;dr: 在这篇文章中,我们在三个坐标轴中的第三个坐标轴上比较了 Pandas 和 SQL:便利性。我们描述了 Pandas dataframe 数据模型更便于数据科学和机器学习用例的六种方式。
在我们的 Pandas 与 SQL 之间的史诗般的战斗的第四部分中,我们说明了 Pandas 如何在数据科学和机器学习方面比 SQL 更方便。熊猫是由数据科学家为数据科学家设计的,并受益于开源数据科学社区热情反馈的数千项改进——所有这些改进都着眼于更大的实用性和易用性。所以很契合也就不足为奇了!
在我们开始之前,如果你错过了我们以前的 Pandas vs. SQL 产品,你仍然可以在这里赶上:第一部分:美食广场和米其林风格的餐厅,第二部分:Pandas 更简洁,和第三部分:Pandas 更灵活。我们之前的帖子关注的是数据框架数据模型和数据框架代数的比较——在这篇帖子中,我们关注的是数据框架的人类工程学:特别是,数据框架是如何使用的。
为了便于查找,下面列出了 Pandas 数据框架比关系/SQL 数据框架更方便的多种方式:
- 在 Pandas 中,您可以逐步构建查询;在 SQL 中,您不能。
- 在熊猫身上,操作和命名中间结果很容易;在 SQL 中更难。
- 在熊猫身上,很容易获得对数据的快速感知;在 SQL 中,这要困难得多。
- Pandas 具有对可视化的本地支持;SQL 没有。
- 熊猫让机器学习变得简单;SQL 没有。
- Pandas 保留顺序以帮助用户验证中间步骤的正确性——并允许用户按顺序操作;SQL 没有。
在这篇文章中,我们将使用来自 Kaggle 的 5 天数据清理挑战的数据集;这是旧金山建筑许可的数据集。数据来自旧金山政府数据门户,他们通过公共领域专用和许可 v1.0 提供数据。
1.在 Pandas 中,您可以逐步构建查询;在 SQL 中,您不能。
Pandas 和 SQL 之间的一个重要区别是,Pandas 允许用户在其他操作的基础上逐步构建更复杂的查询。同时,用户可以检查这些查询片段的中间结果——努力验证它们的正确性。和熊猫一起调试简直易如反掌!
因此,在我们的数据集中,假设我们想要关注 Geary Street 对应的许可证。我们可以如下提取数据集子集:
我们可能已经注意到的一件事是 Geary 跨越了许多社区,这里编码为'Neighborhoods - Analysis Boundaries'
。假设我们只想检查这一列'Neighborhoods - Analysis Boundaries'
(并删除剩余的 42 列),我们可以简单地将子句[['Neighborhoods - Analysis Boundaries']]
附加到前面的表达式的末尾。
这是很多行:1966 年。然后,作为我们的最后两步,假设我们想要识别 Geary 上拥有最多许可的街区。一种方法是追加一个'sort_values'
后跟一个'value_counts'
。
有意思,所以最高的街区是田德隆区,其次是外里士满区。请注意,虽然这一系列操作肯定可以用 SQL 来表达,但这会更加痛苦。我们不能简单地在一个 SQL 查询的末尾添加操作符:查询中有特定的位置需要我们进行修改。例如,要更改显示哪些列,我们需要在早期修改查询的*SELECT*
部分。相反,Pandas 允许你操作性地(或命令性地)思考——一步一步地构建你的最终结果,同时检查中间结果。
2.在熊猫身上,操作和命名中间结果很容易;在 SQL 中更难。
Pandas 嵌入在真正的编程语言 Python 中,借用了许多熟悉的编程习惯用法来操作数据帧。特别是,我们可以将一个数据帧表达式赋给一个变量;这些变量然后可以被操作和/或分配给其他变量。
我们举一个简单的例子来说明。由于该数据集来自数据清理挑战,假设我们怀疑可能有许多空值。我们可以使用以下方法检查每列有多少个:
空值太多了!假设我想创建一个数据集的清理版本,删除包含太多 null 值的列,将阈值设置为 190000 个非 null 值。(整个数据集大约有 199000 行。)
哇——列的数量从 43 列减少到只有 13 列。正如我们在这里看到的,我们能够轻松地定义一个新的变量'sf_permits_cleaned'
(就像我们创建之前的变量'missing_values_count'
),使用标准的编程变量赋值,然后对它进行操作。这种方法对程序员来说很自然。在 SQL 中,可以通过视图实现类似的效果,但是定义视图并对其进行操作不太直观,而且更麻烦。
3.在熊猫身上,很容易获得对数据的快速感知;在 SQL 中,这要困难得多。
Pandas 提供了快速理解数据帧的数据和元数据的方法。我们已经看到过这样的例子,当我们简单地使用变量名或者使用函数'head/tail()'
来打印数据帧时。为了方便起见,为了适合屏幕,某些行和列用'...'
隐藏起来,以帮助用户获得数据的高层次图像。
如果我们想要检查列及其类型的摘要,Pandas 提供的一个方便的函数是'info()'
,它列出了数据集的列、它们的类型以及空值的数量。我们可以使用这个函数来检查我们刚刚创建的数据帧。
因此,看起来唯一仍然包含空值的列是 description 列;所有其他列都已完全填充。
另一个针对数字列的有用的 Pandas 函数是'describe()'
,它提供了这些列的方便摘要,包括计数、平均值、标准偏差和分位数。
嗯,看起来街道号是 0。好奇!
不幸的是,SQL 没有提供类似的便利来理解数据集的形状和特征——为此,您必须编写自定义查询。对于前面的例子,这个查询的长度将与数字列的数量成比例。
4.Pandas 具有对可视化的本地支持;SQL 没有。
对数字表格的分析只能帮到你这么多。通常你需要的是在数据框架中理解信息的视觉方式。与需要将数据加载到单独的可视化或 BI(商业智能)工具中的 SQL 不同,Pandas 在库中提供了内置的可视化支持。例如,我可以简单地调用'plot()'
来查看各种许可的'Current Status'
的条形图。
看起来绝大多数许可证属于已完成、已签发和已归档类别,少数属于其他类别。
这个特性的强大之处是显而易见的:与 SQL 数据库不同,如果您想生成可视化效果,您不需要离开这个库——您可以在这里完成!如果你想“增强”你的可视化体验,有许多与熊猫紧密集成的可视化库,包括 Matplotlib 、 seaborn 和 altair 。如果你像我一样懒惰,根本不希望编写任何代码来生成可视化,那么你可以使用我们的 Pandas-native 可视化推荐库 Lux ,来自动为你生成可视化,并根据你的数据集进行调整。点击阅读更多关于力士的信息。
5.熊猫让机器学习变得简单;SQL 没有。
机器学习是数据科学的关键组成部分,使用户不仅能够理解图像、视频和文本等非结构化数据,还能预测未来。由于 Pandas 与数据科学生态系统紧密集成,因此它与机器学习库(包括常见的库,如 scikit-learn 、 pytorch 、 numpy )配合良好也就不足为奇了。这里,我们将使用 spaCy 库,一个相对较新的自然语言处理库,来理解数据集中的文本列。SpaCy 提供各种单词预训练模型来执行单词嵌入、命名实体识别、词性标注、分类等。要安装 spaCy,我们运行以下命令:
现在我们已经安装了它,假设我们想要了解活动的类型(例如,拆除、移除、替换等。)涉及我们数据集中的每个许可申请(即 row)。这在前面很难理解,但是隐藏在文本字段'Description'
中。让我们使用这个包来提取这个字段中提到的动词列表。作为其中的一部分,我们首先加载 spaCy 的'en_core_web_md'
模型,然后使用该模型提取描述的标记化中的每个动词,并将其存储在一个数组中,如下所示。
因此,正如我们在上面看到的,该模型在提取动词方面做得很好,尽管它确实漏掉了一些(例如,install)。随着大型预训练模型(例如,transformer 模型)的不断增加,我希望这些模型能够更好地集成到 pandas 的日常数据处理中。
在 SQL 数据库中集成机器学习非常困难。虽然一些数据库提供了特定于机器学习的构造(例如,BigQuery ML),但是用户在他们能够完成什么方面受到限制,并且没有细粒度的控制。另一种组装方法是使用 UDF 来进行机器学习。通常最终发生的是用户将他们的数据导出到数据库上下文之外来执行机器学习。
6.Pandas 保留顺序以帮助用户验证中间步骤的正确性——并允许用户按顺序操作;SQL 没有。
熊猫维持秩序。这对于调试和验证非常重要,因为我们正在构建更复杂的查询表达式。继续我的例子,在提取动词的空间之后,假设我想使用'explode'
函数将前面数据帧中的单个动词展开成多行,每一行一个动词;我可以简单地这样做。
注意,我现在有三行对应于原来的第一行,每一行提取一个动词。这种顺序的保持使得验证该步骤的正确性变得容易。使用 SQL 数据库,这将更加困难,因为顺序没有保证,所以需要查看整个输出以了解给定行的结束位置(或者添加一个*ORDER* *BY*
子句来强制执行特定的输出顺序)。
结论
在这篇文章中,我们从最终用户的角度介绍了 Pandas 比 SQL 更方便的各种方式。这包括通过保持顺序、增量合成、命名和操作以及沿途检查来正确地构建 Pandas 查询。这还包括与其他数据科学和数据分析需求的集成,包括可视化和机器学习:Pandas 不仅允许用户完全在 Pandas 中可视化和执行预测建模,还提供了将输出连接到其他流行的可视化和机器学习库和包的挂钩,特别是在 PyData 生态系统中。最终,Pandas 位于成熟的编程语言 Python 中,并继承了它的所有功能。
如果你能想到其他例子,Pandas 比 SQL 更方便,或者相反,我们很乐意听到!请随意回复我们的推文,并且在 Twitter 或 LinkedIn 上关注我们,了解更多熊猫/ Python /数据科学内容!
panda sql——用 Python 运行 SQL 查询的有趣方式
原文:https://towardsdatascience.com/pandasql-interesting-way-to-run-sql-queries-in-python-18a4fc36406a
熊猫
使用 Python Pandas 中的 SQL 查询对数据进行提取、分组、排序和连接数据集!!
pandas 是一款快速、强大、灵活且易于使用的开源数据分析和操作工具,构建于 Python 编程语言之上。
这就是为什么 Pandas 是 Python 的一个广泛使用的数据分析和操作库。🏆
尽管如此,有时 SQL 查询看起来非常简单易懂。另外,如果你学习 SQL,用 SQL 提取、操作和分析数据似乎非常容易。对于所有这样的用户(包括我), pandasql 绝对是一个惊艳的库。
**pandasql**
允许您使用 SQL 语法查询 pandas 数据帧。这也不需要安装或连接任何 SQL server。💡
这可能是使用 Pandas 学习 SQL 的一个小小的动机和简单的开始。
我在上一个项目中使用了 pandasql ,它简化了整个数据提取和分析的方式让我感到惊讶。它提供了一种更熟悉的操作和清理数据的方式。
在本文中,我用几个经典的例子解释了 pandasql 在数据分析中的真正潜力。
要开始,我们需要做的就是用 Python 安装pandasql
库,
**pip install pandasql**
一边用笔记本打字一边在线下的一个单元格中点击Shift
+ Enter
。
**!pip install pandasql**
pandasql
中的主要功能是**sqldf**
。因此,可以使用,
**from pandasql import sqldf**
**sqldf**
有两个参数,其中一个是完全可选的(事实上我从未使用过)。所以,重要且唯一的参数是一个 SQL 查询字符串 。
sqldf 使用它的语法**sqldf(sql_query)**
给出一个 pandas 数据帧作为输出。我们可以使用 SQL 查询任何 pandas 数据帧,就像我们使用 SQL 从任何表中提取数据一样。
***pandasql***
使用 SQLite 语法!
就是这样!!我们都准备好去探索了!🚀
为了使文章有趣并易于理解,我想使用pandas.read_csv()
在 pandas DataFrame 中导入一个虚拟销售数据(自己创建的数据集)。
import pandas as pd
from pandasql import sqldfdf = pd.read_csv("Dummy_Sales_Data_v1.csv", sep=",")
df.head()
虚拟销售数据|作者图片
它是一个 10000 x 12 的数据集,仅包含虚拟数据,可用于所有目的的数据分析。你可以免费下载并用来练习数据分析。
为了便于访问数据,我将重命名这些列。
df.rename(columns={"Shipping_Cost(USD)":"ShippingCost_USD",
"UnitPrice(USD)":"UnitPrice_USD",
"Delivery_Time(Days)":"Delivery_Time_Days"},
inplace=True)
df.info()
按作者更改列名|图像
现在,让我们先从简单的事情开始。
数据析取
从简单的例子开始,让我们从上面的数据框架df
中提取一些数据,并制作两个小表— df_orders
和df_products
。
这可以是sqldf
最简单的例子。
此任务的 SQL 查询将是,
SELECT OrderID, Quantity, Sales_Manager, Status, Shipping_Address, ShippingCost_USD FROM df
要使它成为一个字符串,必须用“ ”
括起来。作为一个好的实践,我将把这个字符串赋给一个变量*query*
。
query = "SELECT OrderID, Quantity, Sales_Manager, Status, Shipping_Address, ShippingCost_USD FROM df"
现在,是时候使用sqldf
功能了!
df_orders = sqldf(query)
df_orders.head()
作者使用 sqldf | Image 提取的 df_orders
类似地,另一个数据帧df_products
也可以使用下面的sqldf
生成。
query = "SELECT OrderID,\
Quantity, \
Product_Code, \
Product_Category, \
UnitPrice_USD \
FROM df"
df_products = sqldf(query)
df_products.head()
df _ 产品使用 sqldf |图片作者
用一行代码编写一个 SQL 查询可能会很繁琐和令人困惑,尤其是当我们想要查询多个列时。
由于我们将 SQL 查询作为一个字符串传递给sqldf
,我们可以使用多行字符串,就像我在上面创建df_products
表时使用的一样。
它使代码变得干净,易于编写、阅读和修改!
这里是 Python 中关于 的一行程序 的趋势之一。
</5-most-powerful-one-liners-you-should-know-in-python-programming-c9d49a89b7f3>
接下来,sqldf
也可以用于有条件地提取或选择数据。
基于条件选择数据
可以根据一列或多列上的条件提取数据。在sqldf
中,这可以通过使用 WHERE 语句来实现。
例如,从*df_orders*
中选择所有订单,其中发货地址为肯尼亚。
让我们首先编写如下的 SQL 查询,
SELECT * FROM df_orders WHERE Shipping_Address = 'Kenya'
将其转换为字符串并传递给sqldf
函数,我们可以提取所需的数据,如下所示。
query = "SELECT * \
FROM df_orders \
WHERE Shipping_Address = 'Kenya'"
df_kenya = sqldf(query)
df_kenya.head()
在 SQL | Image by Author 中使用 WHERE 语句
让事情变得复杂一点,让我们试着把所有发货到的订单,的数量少于 40 并且状态为已发货或已交付。
query = "SELECT * \
FROM df_orders \
WHERE Shipping_Address = 'Kenya' \
AND Quantity < 40 \
AND Status IN ('Shipped', 'Delivered')"
让我们将这个查询字符串传递给sqldf
以获得输出。
df_kenya = sqldf(query)
df_kenya.head()
在 WHERE 语句|作者图片中使用 AND 关键字
这表明,我们可以在sqldf
中使用来自 SQL 的所有关键字,例如Status IN (‘Shipped’, ‘Delivered’)
和在 WHERE 语句中使用AND
关键字。
故事并没有到此结束!我们还可以将数据分成不同的组,并执行计算以供进一步分析。
分组依据
在 Python 中,pandas groupby
用于将数据分类,并有效地聚合数据。在 SQL 中,类似的功能由GROUP BY
语句提供。
GROUP BY
语句或 pandas groupby
函数通常与COUNT()
、MAX()
、MIN()
、SUM()
、AVG()
等聚合函数一起使用,按一列或多列对结果集进行分组
pandasql 使我们能够使用 sql 查询在 Pandas 数据帧上应用
groupby
功能。🏆
例如,让我们看看有多少订单被发送到每个送货地址。从逻辑上讲,这是通过按不同的送货地址对数据进行分组,然后计算订单数量来实现的。
因此,SQL 查询是,
SELECT Shipping_Address, COUNT(OrderID) AS Orders FROM df_orders GROUP BY Shipping_Address
将该查询转换成字符串并传递给sqldf
函数,我们将得到如下预期结果:
query = "SELECT Shipping_Address, \
COUNT(OrderID) AS Orders \
FROM df_orders \
GROUP BY Shipping_Address"df_group = sqldf(query)
df_group.head(10)
熊猫分组使用 sqldf |图片作者
现在,这个输出与熊猫组的输出略有不同。
sqldf
给出熊猫数据帧作为输出🏆而熊猫groupby
返回数据帧对象作为输出。
如果是 pandas groupby,*Shipping_Address*
列将是索引列,pandas groupby
的输出也不会是数据帧。因此,为了进一步使用分组和聚合的数据,我们需要将groupby
对象转换为 pandas DataFrame 并重置其索引。
下面是获得相同输出的 pandas groupby
代码。
df_grouped = df_orders.groupby("Shipping_Address")
df_grouped = df_grouped["OrderID"].count()
df_grouped = pd.DataFrame(df_grouped)
df_grouped.reset_index(inplace=True)
df_grouped.rename(columns={"OrderID":"Orders"}, inplace=True)
显然, pandasql 简化了数据分组及其聚合。
此外,可以使用 SQL **ORDER BY**
语句基于一列或多列对数据进行升序或降序排序。
让我们继续使用相同的数据分组示例,并根据订单数量对送货地址进行排序。
query = "SELECT Shipping_Address, \
COUNT(OrderID) AS Orders \
FROM df_orders \
GROUP BY Shipping_Address \
ORDER BY Orders"df_group = sqldf(query)
df_group
使用 sqldf |图片按作者排序熊猫数据帧
简单地说,只需在现有查询的末尾添加 3 个单词,我们就可以快速地按升序排列结果。
这里有一篇关于熊猫groupby
的精彩文章。
</4-awesome-pandas-methods-to-quickly-analyze-any-dataset-65d2252af6e8>
如果你认为这些都是很简单的查询,那么sqldf
会给你一个惊喜。
sqldf
可以执行高级 SQL 查询,如连接。🏆
SQL 中的 JOINs 或 pandas 中的 merge 是用于根据两个或多个数据集之间的相关列来组合它们的工具。
到目前为止,在我的项目中,我使用了所有类型的使用sqldf
的 SQL 连接,看起来超级方便。
让我们看看如何!
合并数据集
如果我们使用 pandas,有两个函数来组合数据集— merge
和concat
。并且有时会弄不清何时使用哪一个。
SQL 通过使用JOIN
语句简化了事情。并且sqldf
也可以处理JOIN
语句。
例如,让我们将所有订单、送货地址和产品类别放在一起。为此,我们将使用本文开头创建的df_orders
和df_products
。
query = "SELECT T1.OrderID, \
T1.Shipping_Address, \
T2.Product_Category \
FROM df_orders T1\
INNER JOIN df_products T2\
ON T1.OrderID = T2.OrderID"df_combined = sqldf(query)
df_combined.head()
使用 sqldf | Image by Author 的内部联接
这样, df_orders
和df_products
都连接在 OrderID 列上。由于是一个INNER JOIN
, OrderID s,两个数据帧中都存在的将被返回。
同样,sqldf
也可以执行左和右和JOIN
s。
以下是理解如何在 Python pandas 中组合数据集的推荐读物。
总结一下,
我个人经常使用sqldf
,因为它保持代码干净、简单、易于理解和修改。因此,我展示了我们可以用它做什么。
这是关于pandasql
最全面的指南之一,这里是一个完整的 笔记本 以及所有代码。如果我错过了什么,请在评论中告诉我。
现在你可以通过 在这里报名成为中会员 阅读我&其他作家发表的所有故事。当你这样做,我会得到你的费用的一小部分。欢迎加入我的 电子邮件列表 也可以随时更新我的写作。
🚩不要忘记将您的 SQL 查询放在“ ”
中,以便在将其传递给sqldf
之前将其转换为字符串。
🚩在一行中编写代码时,你应该记住PEP-8关于一行中最大字符数的准则,即 79 个字符。
感谢您的阅读!!
熊猫的分组功能是一个强大的工具,但如果你不小心,可能会误导你的观众
你需要明智地决定如何显示 groupby 的结果,否则你的读者可能会从你的数据中收集到错误的见解。
数据科学家最重要的工作之一是向他们的受众有效地传达关于复杂数据的见解。可以说,这样做包括总结数据和呈现“大画面”。换句话说,向你的观众展示一张巨大的原始数据表是没有用的。
想象一下,去参加一个关于房地产市场的讲座,并看到一个没完没了的表,其中列出了该国的每栋房子,以及附加列中给出的相关统计数据,如价格、建造年份等。这将是一场噩梦。这些数据的组织和提炼应该由数据科学家事先完成,而不是由观众按需完成。
进入 Pandas 中流行的groupby
函数 Python 用户中流行的生成汇总数据的方法。关于groupby
的一般用法和应用已经写了很多文章,但是今天我想探讨一个更微妙的问题。如果您不注意分组后如何显示数据,您可能会无意中误导您的观众相信一些不真实的东西。
快速回顾 GroupBy
在进入本文的主题之前,我将简要回顾一下groupby
是如何工作的。从技术上讲,大多数程序员在使用这个函数时所做的事情最好称为“分组和聚集”groupby
的工作方式是,它接受一个列来组成您的组,然后它用一些指定的函数聚集您选择的其他列。
这个句子有点复杂。让我们用一个例子来说明这一点。假设我们有下面这个名为grades
的数据帧,它包含了大学各年级学生的成绩信息:
一组假设学生的期中和期末成绩。
现在,假设我们对找出每年的最高期中分数感兴趣。下面的代码使用groupby
来完成这项任务:
grades.groupby('Year')[['Midterm Score']].max()
上面代码的输出。
以上代码片段中的一些要点:
- 我们分组的列是
'Year'
。从编程角度来说,这意味着该列成为 DataFrame 的新索引。从概念上讲,这意味着我们接下来指定的任何统计数据都将在基于该列对值进行分组之后进行计算。 - 分组之后,我们使用
[['Midterm Score']]
选择我们想要聚合的列。我们使用双括号的原因是我们的代码返回一个数据帧而不是一个序列。 - 在这一点上,Pandas 已经定义了两个迷你数据帧,看起来像下面这样(注意你不能很容易地看到它们;它涉及到一些底层
groupby
对象的欺骗:
熊猫在引擎盖下定义的两个迷你数据框。
- 最后,
.max()
从这些值中选出最大值(即,根据聚合函数,将每个唯一组的所有值聚合成一个值),并将它们组合回我们上面看到的分组聚合数据帧。 - 完成同样任务的更完整的语法是
grades.groupby('Year')[['Midterm Score']].agg(max)
。如果您想使用用户定义的函数,而不是内置的函数,这就是您需要使用的语法。
现在,让我们进入主题。
为什么如果你不小心的话,GroupBy 会误导人
您会注意到,在上面的例子中,我在通过max
函数聚合值之前提取了期中成绩。这样做的明显原因是为了保持简单,为了便于说明,只关注一个专栏。
然而,潜在的原因是,如果不这样做,就会产生误导性的输出。让我们看一个例子。我们使用与上面相同的grades
数据帧,但是这次运行下面的代码片段:
grades.groupby('Year').max()
这为我们提供了以下数据帧作为输出:
这一次,我们也保留了最终分数。
乍一看,这似乎很好。它似乎完成了与上面代码完全相同的事情,除了这次我们还获得了每个年级的最高最终分数。有什么问题?
退一步,以一个不了解数据处理的人的身份来看待数据框架——一个在手机上随意滚动、阅读一篇关于大学生成绩模式的文章的人。
对这样一个人来说,上面的数据似乎暗示着有一个大一新生期中得了 97 分,期末考了 99 分。然而,如果我们回头看看原始数据,情况并非如此。两个不同的学生获得了这些分数,而它们恰好是各自考试的最高分。
这是一个重要的观察。当你聚合多个列作为你的分组的一部分时,Pandas 会单独对待它们*。因此,新列可能相互匹配,也可能不匹配。例如,事实上有一个二年级学生分别得了 88 分和 100 分。*
如果我们添加更多的列,这个问题会变得更加突出。下面,我们有和以前一样的数据框架,除了这个还包括学生姓名:
同样的数据,但是现在有了名字。
让我们在这个名为grades_with_names
的新数据帧上运行相同的代码:
*grades_with_names.groupby('Year').max()*
输出。
现在,好像有一个叫赞达亚的大二学生期中考试得了 88 分,期末考试得了 100 分。事实上,有一个叫赞达亚的大二学生,但那些不是她的分数。顺便提一句,如果你想知道为什么max
在'Names'
栏中有效,那是因为它将字母表中后面的字母视为“更大”
上面的例子说明了在大多数情况下,当作为您的groupby
的一部分进行聚合时,最好是隔离出一个特定的列。如果你选择不这样做,不管是出于什么原因,你都需要向将来可能查看你的数据的人非常清楚地表明这一点。
最后的想法
作为数据科学家,仔细思考我们向受众传达的信息非常重要。当我们长期使用数据时,往往很容易忽略无意中误导我们的数据。我们的头脑对数据足够熟悉,可以避免欺骗。
然而,对我们的观众来说却不是这样。他们中的许多人将是第一次看到这些数据,我们有责任确保他们所显示的内容是准确的,并且易于解释。
因为groupby
是熊猫汇总数据的主要方式之一,所以在广泛使用它之前,了解它的内部运作是合适的。当有人第一次向我解释上述问题时,它被证明对我如何考虑数据表示非常有帮助。希望对你也有帮助。
下次见,伙计们!
想擅长 Python? 获取独家、免费获取我简单易懂的指南点击 。
请考虑使用我下面的推荐链接注册成为正式的媒体会员。你一个月就能看无限的故事,你的会员费直接支持我和其他作家。
论文:“EpiLPS:估计时变再生数的快速灵活的贝叶斯工具”
如何平滑流行病曲线并灵活估计时变再生数
作者的情节
介绍
我的一位同事(也是朋友)最近在 PLoS 计算生物学上发表了一篇题为“EpiLPS:一种快速灵活的估计时变繁殖数的贝叶斯工具”的研究论文。
我没有分享我没有参与的研究论文的习惯。尽管如此,我还是想为这一次破例,因为我坚信这篇论文中开发的方法值得被了解,尤其是对任何从事流行病学工作的人。
下面是这篇文章背后的动机,以及对模拟和真实数据(美国住院数据)的说明。更多信息可以在的论文和相关的网站上找到。
动机
epi LPS(Gressani et al . 2022)是时变再生数 Rt 的灵活贝叶斯推断的方法论;在时间 t 时由受感染病原体产生的继发性病例的平均数量。这是一个关键的流行病学参数,可告知传染病的传播潜力,可由公共卫生当局用于评估干预措施的有效性,并为未来的控制策略提出方向。
这一指标在新型冠状病毒疫情期间广受欢迎,媒体广泛报道,因为其含义易于直观理解。简而言之,当 R < 1, the signal is encouraging as the epidemic is under control and will eventually vanish. On the contrary, a value of R > 1 意味着疾病持续传播,感染正在目睹扩张性影响。因此,拥有一个强大而可靠的工具来根据传染病数据计算繁殖数是至关重要的。
来自哈瑟尔特大学(比利时)、莱顿大学(荷兰)和伯尔尼大学(瑞士)的 EpiPose 团队的一组研究人员最近开发了一种新方法,用于根据给定序列间隔分布的发病率时间序列数据(从感染者出现症状到继发病例出现症状之间经过的时间)来估计瞬时再生数。他们将他们的方法称为 EpiLPS,用于“Epidemiology modeling withLapla cian-P-Splines”,因为拉普拉斯近似和 P 样条平滑器是形成所提出的方法的主干的关键成分。
作者图片
EpiLPS 模型假设观察到的报告病例(按报告日期或症状发作日期)受负二项分布支配。因此,与泊松模型相反,它允许考虑过度分散的特征。流行病曲线在第一步中用 P 样条(其中潜在变量的后验估计通过拉普拉斯近似计算)平滑,并且在第二步中使用更新方程模型作为再生数和通过“插入”方法估计的样条系数之间的桥梁。
作者还解释了 EpiLPS 和 EpiEstim 之间的主要区别,epi estim 是一种由柯里等人(2013 )开发的用于实时估计 Rt 的成熟方法,并在不同的流行病情景下对这两种方法进行了广泛的比较。
EpiLPS 的一个有趣特性是,用户可以选择完全“无采样”路径,其中模型超参数固定在其最大后验概率 (LPSMAP)或基于 Metropolis-adjusted Langevin 算法(LPSMALA)的完全随机路径(LPSMALA)。说到效率,拉普拉斯近似和 B 样条求值的例程已经用 C++编写,并通过 Rcpp 包集成到 R 中,因此底层算法可以在可忽略的时间内执行。
下面,我们提供一个简短的例子,说明如何使用 EpiLPS 例程来估计 rt。
入门指南
EpiLPS 包可从 CRAN 获得(参见https://cran.r-project.org/web/packages/EpiLPS/index.html),并可通过输入以下命令从 R 控制台安装:
install.packages("EpiLPS")
然后,可以按如下方式加载该包:
library("EpiLPS")
EpiLPS 包的结构相当简单,因为它包含几个例程:
- 函数
epilps()
是拟合再现数的核心程序。 - 通过
plot.epilps()
,用户可以绘制估计的流行曲线和 Rt。 - 最后,开发了两个辅助程序
episim()
和perfcheck()
来基本上再现相关论文的模拟结果。
模拟的例子
一组流行病数据可以用episim()
程序模拟,方法是指定一个序列区间分布,并在一组可用的模式中选择真实的繁殖数曲线(这里我们选择模式 5,对应于一条相当波动的曲线)。
按照endepi
选项中的规定,模拟疫情将持续 40 天。通过设置选项plotsim = TRUE
,程序返回一个总结事件时间序列的图形、一个指定序列间隔分布的条形图以及真实的基本再生数曲线。
set.seed(1234)SI <- c(0.344, 0.316, 0.168, 0.104, 0.068)
simepidemic <- episim(
serial_interval = SI,
Rpattern = 5,
plotsim = TRUE,
verbose = TRUE,
endepi = 40
)## Chosen scenario: 5 'Wiggly then stable Rt'.
## Incidence of cases generated from a Poisson distribution.
## Total number of days of epidemic: 40.
作者的情节
如果您想了解生成的事件时间序列的概况,只需输入:
simepidemic$y## [1] 10 6 15 24 37 43 54 46 47 28 20 8 10 10 3 5 3 4 6
## [20] 6 15 21 44 75 135 217 329 409 453 487 457 443 297 290 255 246 246 339
## [39] 395 573
平滑流行病曲线和估计 Rt
现在让我们使用epilps()
例程来平滑流行病曲线并估计再生数。
我们将通过 LPSMAP(一种完全无采样的方法)和 LPSMALA(一种完全随机的方法,依赖于具有朗之万动态的 MCMC 算法)来实现这一点,其中我们指定长度为 10000 的链和大小为 4000 的老化。
LPSMAP_fit <- epilps(
incidence = simepidemic$y,
serial_interval = SI,
tictoc = TRUE
)## Inference method chosen: LPSMAP.
## CI for LPSMAP computed via lognormal posterior approx. of Rt.Total number of days: 40\.
## Mean Rt discarding first 7 days: 1.327.
## Mean 95% CI of Rt discarding first 7 days: (1.164,1.527)
## Elapsed real time (wall clock time): 0.229 seconds.LPSMALA_fit <- epilps(
incidence = simepidemic$y, serial_interval = SI,
method = "LPSMALA", chain_length = 10000, burn = 4000
)## Inference method chosen: LPSMALA with chain length 10000 and warmup 4000.
## MCMC acceptance rate: 56.41%.
## Geweke z-score < 2.33 for: 32 / 33 variables.
## Total number of days: 40\.
## Mean Rt discarding first 7 days: 1.326.
## Mean 95% CI of Rt discarding first 7 days: (1.117,1.555).
## Timing of routine not requested.
执行后,每个例程在控制台中打印用户请求的方法的简要摘要。
对于 LPSMALA,总结了链长、接受率(应该在 57%左右)等基本信息。从打印输出可以看出,模拟流行病的平均再生数约为 1.32。
比方说,我们现在可以使用LPSMALA_fit
对象和plot()
例程来获得平滑的流行病曲线和估计的再生数(默认情况下,可信区间为 5%的显著性水平,但这可以由用户更改)。
days <- seq(8, 40)#--- Smoothed epidemic curve
gridExtra::grid.arrange(
plot(LPSMALA_fit,
plotout = "epicurve", incibars = TRUE, themetype = "light",
epicol = "darkgreen", cicol = rgb(0.3, 0.73, 0.3, 0.2),
epititle = "Smoothed epidemic curve", titlesize = 13, barwidth = 0.25
), #--- Estimated reproduction number
plot(LPSMALA_fit,
plotout = "rt", theme = "light", rtcol = "black",
titlesize = 13, Rtitle = "Estimated R (LPSMALA)"
),
nrow = 1, ncol = 2
)
作者的情节
该图可以通过多种方式定制:
- 用户可以在
themetype
下指定主题。可用选项有gray
(默认)、classic
、light
和dark
。 - 其他选择,如是否显示发病率柱、可信区间包络的颜色、平滑流行病曲线的颜色和估计再生数也是可用的。
上图是在[ggplot2](https://statsandr.com/blog/graphics-in-r-with-ggplot2/)
包中生成的,但是还有另一种直接从LPSMAP_fit
和LPSMALA_fit
对象中提取信息的方法。事实上,可以提取并绘制每天的估计再现数值及其相关可信区间。
下面,我们进行练习并绘制分别用 LPSMAP 和 LPSMALA 获得的估计 Rt,并将其与真实的基本再生数曲线进行比较。合身度挺好的。
par(mfrow = c(1, 2))#--- LPSMAP vs target R
plot(days, sapply(days, simepidemic$Rtrue),
type = "l", lwd = 2, ylim = c(0, 4),
ylab = "Estimated R", xlab = "Time"
)
polygon(
x = c(days, rev(days)), y = c(
LPSMAP_fit$epifit$R95CI_low[8:40],
rev(LPSMAP_fit$epifit$R95CI_up[8:40])
),
col = rgb(0.23, 0.54, 1, 0.3), border = NA
)
lines(days, LPSMAP_fit$epifit$R_estim[8:40], type = "l", col = "cornflowerblue", lwd = 2)
lines(days, sapply(days, simepidemic$Rtrue), type = "l", lwd = 2)grid(nx = 10, ny = 10)
legend("topright",
lty = c(1, 1), lwd = c(2, 2),
col = c("black", "blue", rgb(0.23, 0.54, 1, 0.3)),
c("Target R", "LPSMAP", "LPSMAP 95% CI"), bty = "n", cex = 0.9
)#--- LPSMALA vs target R
plot(days, sapply(days, simepidemic$Rtrue),
type = "l", lwd = 2, ylim = c(0, 4),
ylab = "Estimated R", xlab = "Time"
)
polygon(
x = c(days, rev(days)), y = c(
LPSMALA_fit$epifit$R95CI_low[8:40],
rev(LPSMALA_fit$epifit$R95CI_up[8:40])
),
col = rgb(1, 0.23, 0.31, 0.3), border = NA
)
lines(days, LPSMALA_fit$epifit$R_estim[8:40], type = "l", col = "red", lwd = 2)
lines(days, sapply(days, simepidemic$Rtrue), type = "l", lwd = 2)grid(nx = 10, ny = 10)
legend("topright",
lty = c(1, 1), lwd = c(2, 2),
col = c("black", "red", rgb(1, 0.23, 0.31, 0.3)),
c("Target R", "LPSMALA", "LPSMALA 95% CI"), bty = "n", cex = 0.9
)
作者的情节
例如,您可以通过键入以下内容来访问上周疫情的结果:
# Estimated R of the last week (with LPSMAP)
round(tail(LPSMAP_fit$epifit[, 1:4], 7), 3)## Date R_estim R95CI_low R95CI_up
## 34 34 0.708 0.663 0.756
## 35 35 0.724 0.676 0.775
## 36 36 0.809 0.755 0.868
## 37 37 0.971 0.908 1.039
## 38 38 1.205 1.135 1.279
## 39 39 1.461 1.384 1.541
## 40 40 1.671 1.557 1.794# Estimated mean number of cases of the last week (with LPSMAP)
round(tail(LPSMAP_fit$epifit[, 5:7], 7))## mu_estim mu95CI_low mu95CI_up
## 34 284 225 357
## 35 254 202 319
## 36 248 196 312
## 37 267 211 338
## 38 319 253 404
## 39 411 325 520
## 40 552 394 774
美国住院数据
为了说明真实数据上的 EpiLPS,我们从COVID19
包中下载了美国在 2021 年 9 月 1 日至 2022 年 9 月 1 日期间的住院数据,并应用epilps()
例程来估计再生数。
install.packages("COVID19")
library("COVID19")# Get data and specify serial interval distribution
USADat <- COVID19::covid19(
country = "US", level = 1, start = "2021-09-01",
end = "2022-09-01", verbose = FALSE
)si <- c(0.344, 0.316, 0.168, 0.104, 0.068)inciUSA <- USADat$hosp
dateUSA <- USADat$date
我们使用带有 LPSMAP(默认)方法的epilps()
例程,绘制平滑的流行病曲线和具有 95%可信区间的估计再生数。
epifit <- epilps(incidence = inciUSA, serial_interval = si, K = 20)## Inference method chosen: LPSMAP.
## CI for LPSMAP computed via lognormal posterior approx. of Rt.Total number of days: 366\.
## Mean Rt discarding first 7 days: 0.994.
## Mean 95% CI of Rt discarding first 7 days: (0.983,1.005)
## Timing of routine not requested.gridExtra::grid.arrange(
plot(epifit,
dates = dateUSA, datelab = "3m",
plotout = "epicurve", incibars = FALSE, themetype = "light",
epicol = "darkgreen", cicol = rgb(0.3, 0.73, 0.3, 0.2),
epititle = "USA smoothed epidemic curve", titlesize = 13
),
plot(epifit,
dates = dateUSA, datelab = "3m",
plotout = "rt", theme = "light", rtcol = "black",
titlesize = 13, Rtitle = "USA Estimated R"
),
nrow = 1, ncol = 2
)
作者的情节
感谢阅读。我希望你会像我一样发现本文中开发的方法很有用。如果您碰巧在自己的研究中使用它,请随时联系我和作者。
和往常一样,如果您有与本文主题相关的问题或建议,请将其添加为评论,以便其他读者可以从讨论中受益。
参考
柯里,安妮,尼尔·M·费格森,克利斯朵夫·弗雷泽和西蒙·柯西梅兹。2013."一个新的框架和软件来估计流行病期间随时间变化的繁殖数."美国流行病学杂志 178(9):1505–12。
Gressani,Oswaldo,Jacco Wallinga,Christian L Althaus,Niel Hens 和 Christel Faes。2022." EpiLPS:一种快速灵活的估计时变再生数的贝叶斯工具." PLoS 计算生物学 18 (10): e1010618。https://doi.org/10.1371/journal.pcbi.1010618
相关文章
原载于 2022 年 10 月 19 日https://statsandr.com。
论文说明:探索用于目标检测的平面视觉变压器主干
vit 作为目标探测骨干的力量
在这个故事中,我们将仔细研究 Meta AI 的研究人员最近发表的一篇论文,其中作者探索了如何将标准 ViT 重新用作对象检测主干。简而言之,他们的检测架构叫做 ViTDet 。
用于普通主干网的特征金字塔网络(FPN)的可视化,如 ViTDet 中的一个,“经典”分级主干网,通常表示为 CNN。来源:[1]
先决条件:目标探测骨干
以前,目标探测器的主干网在网络的不同阶段受益于不同的分辨率。如上图所示,特征图具有不同的分辨率,执行实际物体检测步骤的检测头从中受益匪浅。这些主干在科学文献中通常被称为等级主干。通常,ResNets 或其他 CNN 被称为分层主干,但某些 vit(如 Swin Transformer)也有分层结构。我们今天要看的论文必须处理一种不同的主干结构:由于 vit 由一定数量的变换器块组成,所有变换器块都以相同的维度输出特征,所以它从不自然地输出不同分辨率的特征图。作者在论文中讨论了这个问题,并探索了构建多分辨率 FPN 的不同策略。
从单一分辨率主干生成多分辨率特征
由于 ViTs 自然只为其特征地图提供一种分辨率,作者探索了如何使用 FPN 将该地图转换为不同的分辨率。为了简化内存约束和增加特征输出的全局上下文,作者没有计算所有 ViT 块的自注意。相反,他们选择将变压器分成 4 个偶数部分,例如,对于具有 24 个块的 ViT-L,每个部分组成 6 个块。在每个部分的末尾,他们计算该部分的整体自我注意力,其输出被用作 FPNs 的特征图。
为 ViTDet 开发的不同 fpn。来源:[1]
对于方法(a) ,他们试图通过使用卷积或去卷积,从每个部分的个体全局注意力输出对 1/16 特征图进行上采样或下采样,来构建类似 FPN 的解决方案。它们还增加了横向连接,用连接蓝色方块的箭头表示。
对于方法(b) ,他们通过仅放大和缩小来自全局自我注意模块的最后特征图来构建 FPN。这意味着 FPN 中的所有要素都是根据单个输出构建的。此外,他们再次添加了横向连接。
对于方法© ,他们提出了一个非常简单纯粹的解决方案:对最终的整体注意力输出进行上采样和下采样,并且不增加任何横向连接。这种方法是迄今为止最简单的方法,但正如我们现在将看到的,它非常有效。
不同 FPN 方法的性能比较
让我们开始吧!
不同 fpn 的 COCO 结果。来源:[1]
值得注意的是,在 MS COCO 检测基准上,简单 FPN,方法©,在两种 ViT 尺寸上,对于包围盒回归和实例分割工作得最好。
但是,既然已经有了基于 ViT 的检测网络,为什么还要尝试这样一种简单的解决方案来将普通 ViT 用作检测主干呢?答案现在将变得显而易见。
与最先进的(SOTA) ViT 检测网络进行比较
最近在自我监督预培训领域的研究已经开始释放出 ViTs 令人难以置信的能力。该领域中最有前途的任务之一是挑战网络来重建对象的屏蔽部分,通过屏蔽自动编码器(MAE)论文来实现。我们在我的博客上重温了这篇论文,在这里 随意刷新你的知识。
与之前基于 ViT 的检测器相比,ViT-Det 的 COCO 结果(底部三行)。来源:[1]
MAE 预先训练一个标准的 ViT 来学习重建图像的屏蔽部分。事实证明,这是一种成功的预培训策略。为了将这种优势转移到对象检测,作者创建了 ViTDet 架构。这就是本文的全部目的:释放预训练 ViTs 用于物体检测的能力。结果说明了一切。
从结果表中可以看出,使用 MAE 预训练主干,然后在其上使用简单的 FPN,可以得到基于 ViT 的检测主干的 SOTA 结果。由于 Swin 变压器和 MViT 在没有修改的情况下与自监督预训练策略不兼容,因此它们在 ImageNet 上被监督进行预训练。令人惊讶的是,MAE 预训练比标准的监督预训练释放出更多的性能。因此,作者暗示对象检测研究的未来改进将来自于:不是检测架构本身,而是以自我监督的方式对主干进行更强大的预训练。
在我看来,这代表了物体探测研究的一个关键转变。如果你想阅读更多关于自我监督预训练给计算机视觉领域带来的范式转变的信息,请点击这里 阅读我的故事 。
包装它
我们探索了 ViTDet 架构,这是对传统 fpn 的简单而强大的修改,特别是对 vit 的修改,它释放了自监督视觉变压器的能力,用于对象检测。不仅如此,这项研究还为目标检测研究的一个新方向铺平了道路,其中的重点是从架构转移到预训练技术。
虽然我希望这个故事能让你对 ViTDet 有一个很好的初步了解,但仍有很多东西有待发现。因此,即使你是这个领域的新手,我也会鼓励你自己阅读这些论文。你必须从某个地方开始;)
如果你对论文中介绍的方法有更多的细节感兴趣,请随时在 Twitter 上给我留言,我的账户链接在我的媒体简介上。
我希望你喜欢这篇论文的解释。如果你对这篇文章有任何意见,或者如果你看到任何错误,请随时留下评论。
最后但同样重要的是,如果你想在高级计算机视觉领域更深入地探索,考虑成为我的追随者。我试着到处张贴一个故事,让你和其他人对计算机视觉研究的最新进展保持兴趣!
参考资料:
[1]李,,等.“探索用于物体检测的平面视觉变换骨干”arXiv 预印本 arXiv:2203.16527 (2022)。
论文解释——具有多分辨率散列编码的即时神经图形图元
开创性的 NeRF 论文在计算机视觉界掀起了风暴,但提出的方法需要对每个新场景进行数小时的训练。NVIDIA 的一种新方法将这一时间缩短到了几秒钟
由项目网站上的视频生成的 GIF,经许可拍摄。戴珍珠耳环的女孩翻新 Koorosh Orooj ( CC BY-SA 4.0 )。迪士尼云模型华特迪士尼动画工作室( CC BY-SA 3.0 )。
介绍
临近 2020 年底,注意到今年人工智能的主要趋势之一,教授兼研究员 Frank Dellaert 将他的一篇博客文章命名为“NeRF Explosion 2020”。Mildenhall 等人在他们的论文 NeRF:将场景表示为用于视图合成的神经辐射场中提出的惊人结果开辟了一条新的研究路线,该研究仍然是计算机视觉中最热门的主题之一,并可能代表许多应用程序的未来,如3D 形状和图像的合成、人体动画、3D 重建和姿态估计。
最初的 NeRF 很棒,但是……非常慢!为单个场景训练模型可能需要几个小时,甚至几天。由 NVIDIA 研究人员托马斯·穆勒、亚历克斯·埃文斯、克里斯托夫·席德和亚历山大·凯勒撰写的论文即时神经图形图元与多分辨率哈希编码 ( 即时 NGP )提出了一种新的方法,即将这一时间从几个小时缩短到几秒钟。
这是一个高级话题,你可能从未听说过 NeRF 或者需要复习一下。那样的话,别担心,我会掩护你的!下面你会发现我的文章,这一切都是从这张纸上开始的;我建议您在继续之前阅读它,因为我不打算重复解释已经讨论过的内容。
免责声明:大部分图片和内容来源于即时 NGP 论文 / 项目网站,在本文中我尽量报道了对其理解至关重要的部分,必要时补充细节。所有图片都是在作者许可下拍摄的,必要时还列出了额外的许可。
计算机图形图元
在解释即时 NGP 引入的新功能之前,有必要介绍四个任务,或者如论文中所称的计算机图形图元,作者将他们的方法与以前的方法进行了比较。
对于即时 NGP 的部分解释,与 NeRF 一样,首先根据一些逻辑对输入进行编码,然后使用多层感知器(MLP)来学习它们与给定输出之间的映射。在这一节中,我们将看到本文中考虑的所有任务的输入和输出是什么。
千兆像素图像近似
图片由报社许可拍摄。戴珍珠耳环的女孩翻新 Koorosh Orooj(CC BY-SA 4.0)。
这项任务旨在测试模型表现高频细节的能力,目标是了解(非常大的)图像的 2D 坐标与其 RGB 颜色之间的映射。基本上,该模型正在学习有效地压缩图像,并逐个像素地重建图像,也就是说,给定一个像素的坐标,它将返回该位置处原始图像的颜色。
例如,上图的分辨率为 20 000 X 23 466 (469 M RGB 像素),而根据我们稍后将介绍的超参数 t 的选择,模型的可训练参数分别为 117 k (T = 2)、2.7 M (T = 2 ⁷)和 47.5 M (T = 2)。
符号距离函数
经报社许可拍摄的图像。
带符号距离函数(SDF)用于将 3D 形状表示为函数的零水平集。想象在感兴趣的表面上的一个点上计算 SDF,在这种情况下,SDF 将等于零,而如果该点在表面之外,则它将具有等于该点与表面的最小距离的正值,如果它在表面之内,则相同,但具有负号。
在这个任务中,MLP 学习从 3D 坐标到离表面的距离的映射。这可用于 3D 形状压缩(类似于之前的任务,但用于 3D 形状)和形状完成等任务,如 DeepSDF:学习形状表示的连续带符号距离函数所示。
神经辐射缓存
图片由报社许可拍摄。
该任务涉及从特征缓冲区预测照片级像素颜色。
在这种情况下,MLP 从蒙特卡洛 路径跟踪器 中学习给定场景的 5D 光场 。
在这种情况下,训练在线发生,在渲染过程中,“过拟合”在后台运行的路径跟踪器的输出。
神经辐射和密度场(NeRF)
图片由报社许可拍摄。
最后,最后一项任务是以发起该研究系列的论文命名的,即时 NGP 也是该研究系列的一部分。在这种情况下,MLP 从图像观察和相应的透视变换中学习给定场景的 3D 密度和 5D 光场。
通过这种方式,可以从新的有利位置创建场景的新视图,这个问题称为 新颖的视图合成 ,逼真地再现灯光效果和材质属性。更多细节请看我之前的文章。
既然我们已经提出了我们想要解决的任务,让我们看看这篇论文的主要贡献是什么。
输入编码
频率编码
NeRF 受早期作品的启发,比如介绍 Transformer 架构的著名论文,将标量位置 x∈R 编码为 L∈N 正弦和余弦函数的多分辨率序列
图片由报社许可拍摄。
如 NeRF 论文中的消融研究所示,如果没有这种编码,高频细节将无法捕捉。
这种编码是固定的,因此不太有表现力,这意味着需要一个“大”MLP来学习前面介绍的复杂任务,因为从输入到输出的任务都委托给它了。
参数编码
最新的先进方法使用参数编码,其思想是在辅助数据结构中安排额外的可训练参数,例如网格或树。然后根据输入查找这些参数,并(可选地)进行插值。
为什么这种方法应该是有益的?我们用较大的内存占用换取较小的计算成本;我们可以拥有一个小得多的 MLP,而不是一个大的,虽然以这种方式消除的参数可以通过使用额外的数据结构以某种方式重新获得,只有其中的一个小的子集会随着每个梯度反向推而更新,从而大大加快了训练速度。这一点很关键,所以让我们看一个例子来更好地理解它。假设我们用 3D 网格划分我们感兴趣的场景,对于网格内的给定点,对于每个反向传播的样本,我们只需要更新 8 个嵌入(对于包含该点的网格中的 3D 体素的每个顶点一个嵌入)。当我们介绍所提出的方法时,这一点将变得更好理解。
即时 NGP 使用的编码就属于这一类;在描述它之前,让我们先看看以前的方法有什么问题。
一种可能的方法是使用类似于 ACORN:用于神经场景表示的自适应坐标网络中的域的树细分,其中大型辅助坐标编码器神经网络被训练为在 x 周围的叶节点中输出密集的特征网格。这种方法虽然比以前的方法产生更大程度的自适应性,但它以更高的计算成本实现,仅当足够数量的输入在每个叶节点结束时才可摊销。
另一种方法是使用可训练特征的密集网格,但是它们比神经网络权重消耗更多的内存。特别是,这种方法是浪费的,因为空白空间的区域被视为靠近表面的区域,这导致参数的数量增长为 O(N),而感兴趣的可见表面的区域仅增长为 O(N)。
如果感兴趣的表面是先验已知的,诸如八叉树或稀疏网格的数据结构可用于剔除密集网格中未使用的特征。然而,在 NeRF 设置中,表面仅在训练期间出现。
对于这些先前作品的所有参考,请参考论文。现在让我们来看看即时 NGP 最重要的组成部分之一。
多分辨率散列编码
图片由报社许可拍摄。
程序概要
论文的主要贡献肯定是提出了输入坐标的编码。
让我们首先简要地总结一下这个过程,然后分别分析细节:
- L d 维 网格被定义。具体来说,如果我们要建模的是一幅图像,至于“Gigapixel”任务,d 等于 2;另一方面,如果我们想要对三维体积建模,至于“NeRF”任务,d 等于 3。在上图中,你可以看到两个 2D 网格的例子,一个是分辨率为 1 / N0 的网格,另一个是分辨率为 1 / N1 的网格,我们将在后面看到这些 N l 是如何选择的
- 每个级 l 与多达 T 个具有维度** F 的特征向量**相关联
- 对于一个给定的样本,其输入坐标 x 被映射到每一层中相应的体素 (d 维单元)。例如,在图像中,x 落在级别 0 的右下角单元格中,而在级别 1 的中间单元格中
- 给定层的顶点数为 V = (Nl + 1)^d,例如,如果 N1 = 3 且 d = 2(就像上图中 N1 将边精确地分为 3),对于层 1,我们有(3 + 1) = 16 个顶点。如果 V ≤T,我们在该层的顶点和特征向量之间有 1:1 的映射。在更精细的级别,其中 V > T,我们使用一个散列函数h将每个 d 维顶点映射到相应级别的 T 个特征向量之一。我们确实可以想到一个散列表,尽管没有明确的冲突处理
- 我们现在有了每层的 d 个特征向量,每个向量都与各自体素的顶点相关联。在这一点上,我们线性内插d 矢量以获得代表该层的最终矢量
- 我们连接每一层的最终矢量,如果有的话,辅助输入(视角方向,材质参数等。),这是将作为输入给一个多层感知器 ( MLP )的向量,从这里开始,程序类似于最初的 NeRF
现在让我们更详细地了解每一步。
选择每个级别的网格分辨率
一个几何级数用于定义在最粗 ( Nmin )到最细 ( Nmax )级别之间的中间分辨率:
图片由报社许可拍摄。
使用简单的对数规则,我们可以证明:
图片由作者提供。
我们看到,如果 l = 0,Nl = Nmin * b⁰ = Nmin,而如果 l = L - 1(级别范围从 0 到 L - 1),Nl = Nmin * Nmax / Nmin = Nmax,则其他值介于两者之间。
由于等级 L 的数量很大,增长因子 b 通常很小。论文的用例有 b ∈[1.26,2]。
空间哈希函数
在一个散列函数中我们想要什么特性?作者列举如下:
- 高效计算
- 导致连贯的查找
- 一致覆盖特征向量阵列,而不管查询点的结构如何
由于这些原因,即时 NGP 使用了一个空间哈希函数 [ Teschner 等人 2003 ]的形式
经报社许可拍摄的图像。
其中⊕denotes 的逐位异或运算和ππI 唯一,大素数。
优化散列函数也被认为是未来的工作,将该方法变成一种字典学习方法。
原文提供了其他细节,并为感兴趣的读者提供了有用的参考。
性能与质量
可以想象,随着 T 的增加,我们拥有更高的质量、更高的内存使用率和更低的性能(更长的训练/推理时间)。该论文强调的有趣方面是,虽然内存随 T 线性增长,但质量和性能往往呈亚线性增长。
同样,L 和 F 也代表了质量和性能之间的权衡。作者发现 F = 2 和 L = 16 对于他们的大多数应用来说都是很好的值。
隐式哈希冲突解决
我们说冲突不被处理,这意味着什么?
首先,我们注意到在低层没有碰撞,因为映射是 1:1。在可能发生这些碰撞的较高层,梯度被平均化。一个重要的观察是,不是所有碰撞的点都同等重要;可见表面上的点将具有更大的梯度,因此对学习的编码具有更大的影响。
值得注意的是,这并非完全没有后果,正如在一项关于使用 MLP 的重要性的最终消融研究中所示,我们看到哈希碰撞会在重建中产生一些噪声,如果我们用线性层替换 MLP,这种噪声会特别明显。
图片由报社许可拍摄。
d-线性插值
需要注意的是插补不是可选的。没有它,我们将有网格对齐的不连续性(块状外观)。线性插值不是人们能想到的最高阶,对于需要更高阶的应用,作者提出了 平滑步骤 函数
图片由报社许可拍摄。
这是更昂贵的 d-二次或 d-三次插值的低成本替代方案。
然而,这种解决方案在本文中从未使用过,根据经验,它会降低所考虑用例的重建质量。
履行
性能考虑因素
这项工作的一个相关方面是架构是如何实现的,在这里我们可以看到 NVIDIA 研究人员在充分利用 GPU 方面的经验。整个系统实现利用了完全融合的 CUDA 内核(融合运算以加速计算),并专注于最小化浪费的带宽和计算操作。散列表条目以半精度存储,以全精度维护参数的主副本,用于稳定的混合精度参数更新。他们还优化了 GPU 缓存,调度计算,以便在移动到下一个输入之前,在一个批处理中查找所有输入的多分辨率哈希编码的每个级别。关于哈希表的大小,他们能够最大限度地提高性能,直到 T ≤ 2 ⁹,超过这个阈值,性能开始显著下降,因为 NVIDIA RTX 3090 GPU 的二级缓存对于各个级别来说变得太小。
这种高度优化的 C++实现比 Python 中的简单实现快 10 倍。
有了这种级别的优化,将实现带来的改进与文章中提出的创新带来的改进区分开来是很重要的;在所有的实验中,作者都小心翼翼地将这两种效应分开,有时会重新实现以前的架构。
架构、初始化和培训
架构、初始化和训练程序都相当标准。我不会在这里报告我建议查阅文件的所有细节。重要的是要注意,同样在这种情况下,在编码之后,我们使用了一个在结构上与原始 NeRF 非常相似的 MLP,一个密度 MLP 后面跟着一个彩色 MLP,但是由于额外的可训练数据结构而变得更小。
实验和结果
图片由报社许可拍摄。
即时 NGP 超越了以前方法的性能和/或增加了所有考虑的任务的灵活性,具有更简单的额外优势。最令人惊讶的结果之一是,他们与的峰值信噪比** ( PSNR )相匹配**
图片由作者提供。这个指标还有其他定义,这是它在即时 NGP 的 GitHub 库中的实现方式。
原来的 NeRF(训练小时数)只需 5–15s,与相比提升了 20–60 倍,这可以归功于提出的哈希编码。
在其他任务中也可以看到类似的结果,例如,即时 NGP 在 2.5 分钟而不是 36.9 小时的训练后,在重建东京的千兆像素图像方面实现了与 ACORN 相同的 PSNR。
总的来说,我们可以说瞬时 NGP 令人难以置信的速度将使得有可能将 NeRF 应用于新的任务,例如,所提出的多分辨率散列编码是将 NeRF 加速几个数量级并匹配并行非神经 3D 重建技术的性能的直接替代。
结论
在本文中,我们探讨了 NVIDIA 的即时 NGP 如何让 NeRF 快如闪电。
如果这项新技术引起了你的兴趣,并且你想亲自尝试一下即时 NGP,你可以!看看官方 GitHub 库并按照所有的说明去做,结果会很惊人。
感谢您读到这里,如果您有任何与本文相关的建议或问题,请随时留下评论或与我联系,我的联系方式如下。
想保持联系,不要错过我的新文章?在中、 LinkedIn 或 Twitter 上关注我。
论文解释:无监督视觉表征学习的动量对比
查看 MoCo v1 和 v2 文件中的原则
在这个故事中,我们将回顾 MoCo 论文,这是一种使用动量对比(MoCo)对计算机视觉模型进行预训练的无监督方法,已经由作者从版本 1 到版本 3 进行了迭代改进。
于 2020 年由何等人在论文 【动量对比无监督视觉表征学习】 中首次提出,并在 【改进的基线与动量对比学习】 *中进一步改进为 v2。*在陈等人的“训练自监督视觉变形器的实证研究”中有另一个称为 v3 的迭代,它介绍了训练过程和模型架构的基本适应,我们将在未来的故事中介绍。
由于这种方法一直在不断改进,我们将首先回顾本文背后的最初想法,然后概述新版本引入的改进。我尽量让文章简单,这样即使没有什么先验知识的读者也能理解。事不宜迟,我们开始吧!
对最初的 MoCo 论文背后的关键思想的简单说明。来源:【1】
先决条件:计算机视觉的自我监督和非监督预训练
在我们深入研究 MoCo 的论文之前,有必要快速回顾一下自我监督的职前培训到底是怎么回事。如果你一直在阅读我的其他自我监督学习故事,或者你熟悉自我监督预培训,请随意跳过这一部分。
传统上,计算机视觉模型总是使用监督学习来训练。这意味着人类看着这些图像并为它们创建各种标签,这样模型就可以学习这些标签的模式。例如,人类注释者可以为图像分配一个类标签,或者在图像中的对象周围绘制边界框。但是,任何接触过标注任务的人都知道,创建足够的训练数据集的工作量很大。
相比之下,自我监督学习不需要任何人为创造的标签。顾名思义,模型学会自我监督。在计算机视觉中,对这种自我监督进行建模的最常见方式是获取图像的不同裁剪或对其应用不同的增强,并通过模型传递修改后的输入。尽管图像包含相同的视觉信息,但看起来并不相同,我们让模型知道这些图像仍然包含相同的视觉信息,即相同的对象。这导致模型学习相同对象的相似潜在表示(输出向量)。
我们可以稍后在这个预训练的模型上应用迁移学习。通常,这些模型然后在 10%的带有标签的数据上进行训练,以执行下游任务,如对象检测和语义分割。
使用动量对比进行无监督学习
在 MoCo 的论文中,无监督学习过程被框定为字典查找:每个视图或图像被分配一个关键字,就像在字典中一样。该密钥通过使用卷积神经网络对每个图像进行编码而生成,输出是图像的矢量表示。现在,如果一个查询以另一个图像的形式呈现给这个字典,这个查询图像也被编码成矢量表示,并且将属于字典中的一个关键字,具有最小距离的关键字。
左边是查询图像和编码器,右边是小批量密钥队列和 momentum 编码器。来源:【1】
训练过程设计如下:编码器网络选择并处理查询图像,以计算编码的查询图像 q 。由于该模型的目标是学习区分大量不同的图像,因此该查询图像编码不仅与一个小批量的编码关键图像相比较,而且与它们中的多个相比较。为了实现这一目标,MoCo 形成了一个由动量编码器网络编码的小批量队列。当选择新的小批量时,其编码被排队,并且数据结构中最旧的编码被出列。这将由队列表示的字典大小与批处理大小分离,并允许查询更大的字典。
如果查询图像的编码与字典中的关键字匹配,则这两个视图被认为来自同一图像(例如,多个不同的裁剪)。
训练目标由信息损失函数表示:
信息对比损失函数。来源:【1】
该损失函数允许模型学习来自同一图像的视图的较小距离以及不同图像之间的较大距离。为了实现这一点,损失类似于基于 softmax 的分类器损失,其目的是将 q 分类为 k+ 。对查询 q 的一个正键和 K 个负键计算总和。
最后,如第一幅图所示,通过编码器网络执行反向传播,但不通过动量编码器。
作者试图用动量编码器对每幅图像实现相对一致的编码。由于保持权重冻结不会反映模型的学习进度,他们建议使用动量更新而不是复制编码器的权重来获得一致的动量编码器结果。为了实现这一点,将动量编码器网络设置为由查询编码器的基于动量的移动平均值来更新。每次训练迭代都执行这种更新。
MoCo v2 的进一步改进
在模型的第二个版本中, MoCo v2 ,在他们的论文 “改进的基线与动量对比学习” *,*中,作者将 SimCLR 论文中的一些贡献转移到他们的模型中。最初,为了在预训练 MoCo 之后训练线性分类器,他们向模型添加了单个线性层。在 MoCo v2 中,该层已被 MLP 投影头取代,从而提高了分类性能。另一个显著的改进是引入了更强的数据扩充,类似于 SimCLR 中的数据扩充。他们通过添加模糊和更强的颜色失真来扩展 v1 增强。
最新版本的 MoCo v3 ,在“训练自我监督视觉变形者的实证研究”论文中介绍,将该模型改编为不同的训练过程。由于这些变化不仅仅是简单的扩展,我们将在以后的文章中讨论这篇文章。
包装它
在本文中,您已经了解了 MoCo,这是一篇最受欢迎的自我监督框架论文,为该领域更有前途的论文铺平了道路。
通常在我的论文故事中,我会在结尾介绍这种方法的结果和表现。这一次,我们将跳过这一部分,因为这两种方法都不是最先进的了,但 MoCo v3 是。尽管如此,我发现了解 MoCo 背后的原理作为最先进的 MoCo v3 论文的基础是非常重要的。
虽然我希望这个故事能让你对 MoCo 有一个很好的初步了解,但仍有很多东西有待发现。因此,即使你是这个领域的新手,我也会鼓励你自己阅读这些论文。你必须从某个地方开始;)
如果你对论文中介绍的方法有更多的细节感兴趣,请随时在 Twitter 上给我留言,我的账户链接在我的媒体简介上。
我希望你喜欢这篇论文的解释。如果你对这篇文章有任何意见,或者如果你看到任何错误,请随时留下评论。
最后但同样重要的是,如果你想在高级计算机视觉领域更深入地探索,考虑成为我的追随者。我试着每周发一篇文章,让你和其他人了解计算机视觉研究的最新进展。
参考资料:
[1]何,,等.“用于无监督视觉表征学习的动量对比”IEEE/CVF 计算机视觉和模式识别会议论文集。2020.https://arxiv.org/pdf/1911.05722.pdf
论文解释:推动自我监督网络的极限:我们能胜过 ImageNet 上没有标签的监督学习吗?
探索 ReLICv2 中的新方法
在这个故事中,我们将看看最近的一篇论文,这篇论文推动了自我监督学习的发展,由 DeepMind 发表,昵称为 ReLICv2。
在他们的出版物*“推动自我监督网络的极限:我们能胜过 ImageNet 上没有标签的监督学习吗?”、Tomasev 等人提出了对他们在 ReLIC 后面的名为“通过不变因果机制的表征学习”*的论文中提出的技术的改进。他们方法的核心是增加了一个 Kullback-Leibler-Divergence 损失,这是用经典对比学习目标的概率公式计算出来的。不仅如此,他们还使用了一个完善的增强方案,并借鉴了其他相关出版物的成功经验。
我试图保持文章的简单,这样即使没有什么先验知识的读者也能理解。事不宜迟,我们开始吧!
遗迹训练管道的插图。来源:【1】
先决条件:计算机视觉的自我监督和非监督预训练
在我们深入讨论之前,有必要快速回顾一下自我监督预培训是怎么回事。如果你一直在阅读我的其他自我监督学习的故事,或者你熟悉自我监督预培训,请随意跳过这一部分。
传统上,计算机视觉模型总是使用监督学习来训练。这意味着人类看着这些图像,并为它们创建了各种各样的标签,这样模型就可以学习这些标签的模式。例如,人类注释者可以为图像分配一个类标签,或者在图像中的对象周围绘制边界框。但是,任何接触过标注任务的人都知道,创建足够的训练数据集的工作量很大。
相比之下,自我监督学习不需要任何人为创造的标签。顾名思义,模特学会自我监督。在计算机视觉中,对这种自我监督进行建模的最常见方式是获取图像的不同裁剪或对其应用不同的增强,并通过模型传递修改后的输入。尽管图像包含相同的视觉信息,但看起来并不相同,我们让模型知道这些图像仍然包含相同的视觉信息,即相同的对象。这导致模型学习相同对象的相似潜在表示(输出向量)。
我们可以稍后在这个预训练的模型上应用迁移学习。通常,这些模型然后在 10%的带有标签的数据上进行训练,以执行下游任务,如对象检测和语义分割。
新颖的贡献和知识的结合
正如许多其他自我监督的预训练技术一样,ReLICv2 训练过程的第一步是数据扩充。在论文中,作者首先提到了以前成功的增强方案的使用。
第一个是在 SwAV 中使用的增强。与以前的工作相反,SwAV 不仅创建了输入图像的两种不同的裁剪,而且多达 6 种裁剪。这些可以制成不同的尺寸,如 224x244 和 96x96,最成功的数量是两个大作物和 6 个小作物。如果你想了解更多关于 SwAV 的增强计划,请务必阅读我关于它的故事。
前面描述的第二组扩充来自 SimCLR。这个方案现在被这个领域几乎所有的报纸所采用。通过应用随机水平翻转、颜色失真、高斯模糊和曝光来处理图像。如果你想了解更多关于 SimCLR 的信息,请务必去看看我的文章。
但是 ReLICv2 还使用了另一种增强技术:从图像中的对象中移除背景。为了实现这一点,他们以无人监管的方式在一些 ImageNet 数据上训练了一个显著背景去除模型。作者发现,当以 10%的概率应用时,这种增加是最有效的。
使用无监督 DeepUSPS 的显著背景去除增强。来源:【2】
一旦图像被增强并且进行了多次裁剪,输出就通过编码器网络和目标网络,它们输出相同维度的特征向量。当使用反向传播更新编码器网络时,目标网络通过类似于MoCo 框架的动量计算接收更新。
ReLICv2 的总体目标是学习编码器网络,以便为相同的类产生一致的输出向量。为了实现这一点,作者制定了一个新的损失函数。他们从标准对比负对数似然开始,其核心是一个相似性函数,将锚视图(主输入图像)与正样本(图像的增强版本)和负样本(同一批中的其他图像)进行比较。
ReLICv2 损失函数由负对数似然性以及锚视图和正视图的 Kullback-Leibler 散度组成。来源:【2】
这种损失通过对比目标的概率公式 : 锚图像和正面的可能性之间的 Kullback-Leibler 差异而扩展。这种强制网络不学习积极的靠近和消极的远离,但是在避免可能导致学习崩溃的极端聚类时,在集群之间创建更平衡的景观。因此,这个附加损失项可以被视为类似于调节。这两项都有一个α和β超参数,允许对这两项损失进行单独加权。
所有这些新奇事物的加入证明是成功的。为了找出是在哪些方面,让我们更仔细地看看论文中给出的结果。
结果
**relic v2 试图证明的要点,正如其在论文标题中所说的,是自监督预训练方法只有在它们都为编码器网络使用相同的网络架构时才具有可比性。**对于他们的工作,他们选择使用经典的 ResNet-50。
在 ImageNet 线性评估方案下使用不同的预训练 ResNet-50 的结果。来源:【2】
当使用相同的 ResNet-50 并在 ImageNet-1K 上训练其线性层,同时冻结所有其他权重时,ReLICv2 的输出远远超过现有方法。引入的改进甚至导致相对于原始遗物纸的性能优势。
与不同数据集上的监督预训练模型相比,准确性有所提高。来源:【2】
当在其他数据集上比较迁移学习性能时,ReLICv2 与其他方法(如 NNCLR 和 BYOL)相比,继续表现出令人印象深刻的性能。这进一步表明 ReLICv2 是一种新型的自我监督预训练方法。对其他数据集的评估在其他论文中不常提及。
ReLICv2 和 BYOL 对习得集群的阐释。点越蓝,学习到的越接近对应的类簇。来源:【2】
另一个生动的图形显示了 ReLICv2 学习的职业比其他框架如 BYOL 学习的职业更接近。这再次表明,与其他方法相比,该技术具有创建更细粒度集群的潜力。
包装它
在本文中,您已经了解了 ReLICv2,这是一种用于自我监督预训练的新方法,已经显示出有希望的实验结果。
通过结合对比学习目标的概率公式,并通过添加经过验证的增强方案,该技术已经能够将视觉中的自我监督预训练空间向前推进。
虽然我希望这个故事让你对 ReLICv2 有了一个很好的初步了解,但仍有很多东西有待发现。因此,即使你是这个领域的新手,我也会鼓励你自己阅读这些论文。你必须从某个地方开始;)
如果你对论文中介绍的方法有更多的细节感兴趣,请随时在 Twitter 上给我留言,我的账户链接在我的媒体简介上。
我希望你喜欢这篇论文的解释。如果你对这篇文章有任何意见,或者如果你看到任何错误,请随时留下评论。
最后但同样重要的是,如果你想在高级计算机视觉领域更深入地探索,考虑成为我的追随者。我试着每周发一篇文章,让你和其他人了解计算机视觉研究的最新进展。
参考资料:
[1]米特洛维奇、约瓦娜等人,“通过不变因果机制的表征学习”arXiv 预印本 arXiv:2010.07922 (2020)。https://arxiv.org/pdf/2010.07922.pdf
[2] Tomasev,Nenad,等人“推动自我监督的结果网的极限:在 ImageNet 上没有标签的情况下,我们能胜过监督学习吗?." arXiv 预印本 arXiv:2201.05119 (2022)。https://arxiv.org/pdf/2201.05119.pdf
论文综述:视觉异常检测的修复重建
用 5 分钟时间解释如何通过将带有随机图案的图像传送到自动编码器来改进异常检测
在 Unsplash 上由 Neven Krcmarek 拍摄的照片
在这个故事中,我将回顾由卢布尔雅那大学介绍的修复异常检测 (RIAD)方法【1】。本文中有两个主要概念:
- RIAD 是一种方法,去除图像中的部分局部区域,并重建由受损图像开始的图像
- RIAD 基于一个编码器-解码器网络,该网络学习区分无异常图像和有缺陷图像。
概述
1。先决条件
在深入解释本文之前,有一些概念需要了解。由于论文使用了自动编码器,你需要了解它是什么。查看我的文章来了解它是如何工作的。
- U-net
- auto encoder 如何应用于异常检测?
- 自动编码器在异常检测中的局限性
- 自动编码器的重建损失
- 结构相似度
- 梯度幅度相似度
优信网
U-net 是 Ronneberger 在 2015 年推出的卷积编解码网络[4]。它由两个网络组成:压缩数据模式的编码器和解压缩并重建原始数据的解码器。这个网络的主要特点是它的架构。在编码器架构中,每个卷积层中的特征通道数量加倍,而在解码器架构中,通道数量减半。此外,U-net 使用跳过连接来通过网络的不同层传输功能。这是导致精确重建的一个重要方面。
为了更好的了解细节,有一篇@ Sik-Ho Tsang 写的评论解释了 U-net 的架构。
autoencoder 如何应用于异常检测?
- 对于银行、保险和制造业等众多领域而言,异常检测是一项至关重要的挑战。在这种情况下,自动编码器可以很好地解决这类问题,因为它们可以处理无人监督的问题。
- 在训练期间,您只将正常数据传递给自动编码器。这样,模型将从提供的训练数据中学习潜在的表示。我们还将假设在训练期间没有观察到的异常数据、不应该被自动编码器很好地重建,因此与正常数据相比应该具有高重建误差。
- 例如,可以应用自动编码器来解决欺诈检测问题。在训练期间,我们只将正常的交易传递给模型。当我们评估测试集中的模型时,大多数欺诈性交易可能具有高于正常交易的均方误差。
自动编码器在异常检测中的局限性
- 自动编码器倾向于学习“身份”功能
- 然后,它能够重新创建异常数据,即使它从未被训练过。
自动编码器的重构损耗
训练自动编码器的损失函数被命名为重建损失。最流行的重建损失是均方误差 (MSE) 损失函数。它计算原始输入和网络输出之间的平方差的平均值。如果应用于图像,MSE 测量我们正在比较的图像的平均像素差异。MSE 越高,图像越不相似。
MSE 的替代方案是 L1 函数,其测量输入和输出之间的绝对差值的和
虽然这些重建损失是常用的,但它们有一些局限性。它们假设相邻像素之间的独立性,这通常是不被尊重的,并且不足以定量地测量原始样本和网络输出之间的相似性。
结构相似性
为了考虑局部相关性,而不是单个像素值的差异,许多论文中提出了 SSIM (结构相似性)损失【2】。SSIM 测量一对图像补片 p 和 q 之间的距离,其特征在于三个不同的方面:
- 亮度,通过计算补丁的平均值来考虑。
- 对比度是补丁方差的函数。
- 结构通过计算两个面片的协方差来考虑。
其取值范围介于-1 和 1 之间。当分数接近 1 时,这意味着两个被比较的图像是相似的,而分数为-1 表示它们非常不同。
梯度震级相似性
像 SSIM,梯度幅度相似性是一个补丁相似性度量。它考虑了比较图像之间的局部相似性[3]。不是比较图像像素,而是考虑两幅图像 I 和 I_{R}上的局部梯度。
其中 g(I)是图像 I 的梯度幅值图,c 是常数以确保数值稳定性。
沿原始图像的 x(或水平)和 y(或垂直)方向卷积 3x3 Prewitt 滤波器 得到原始图像的梯度幅值图。
2.MVTEc 广告数据集
MVTEc 广告数据集[5]
MVTec AD 是一个新颖而全面的工业数据集
,由 5354 幅高分辨率图像组成,分为 15 类:5 种纹理和 10 种物体【5】。训练集仅由正常的
图像组成,而测试集包含有缺陷和无缺陷的图像。
图像分辨率在 700x700 和 1024x1024 像素之间变化
3.里亚德算法
RIAD 方法概述[1]
在无异常的图像上训练自动编码器,其中随机选择的区域被设置为 0。如前所述,使用的编码器-解码器架构是 U-net。但是这些图像是如何被掩盖的呢?这些是以下步骤:
- 我们通过输入区域大小参数 k 从每幅图像中随机选择一组像素。
- 每个图像可以被认为是一个尺寸为高度/k *宽度/k 的网格。
- 我们的图像/网格被随机分成 n 个不相交的(或不重叠的)集合 Si 。
- 我们生成一个二进制掩码,M_{Si},如果局部区域属于集合 Si,它包含 0,否则包含 1。
- 最终图像是二进制掩模和原始图像的乘积。
从图像中随机移除像素的算法[1]。
在 U-网的训练过程中,大小 K 是从一组 K 值中随机选取的。在本文中,他们使用 K = {2,4,8,18}作为一组区域大小。在将掩模应用于原始图像之后,通过将修补的图像提供给模型来获得重建,然后通过考虑三种类型的损失来计算总损失:
- MSGMS 损失,即几个尺度下的平均 GMS 距离:
其中 Hl 和 Wl 分别是被比较图像的高度和宽度,Nl 是比例 l 下的像素数
- SSIM 损失是所有本地 SSIM 值的平均值:
- 逐像素 L2 损失(或均方误差损失)
U-net 的训练[1]。
然后,我们可以获得在每个 k 值下为每个图像重建 I_{R}产生的异常图 G_A(I,I_{R}) 的平均值。随后,我们计算图像级异常分数,其本质上是异常图平均值的最大值。
对 U-net 的评价[1]。
评估期间获得的异常图示例说明[1]。
比如我们假设 K = {2,4,8,16}。我们计算属于{2,4,8,16}的每个 k 值处的每个图像重建的异常图,然后,我们计算这些异常图的平均值,如最后一列所示。
4.结果
异常检测的图像水平 ROC-AUC 评分[1]。
- 与其他最先进的模型相比,RIAD 在对象类别上表现更好,如瓶子、金属螺母和牙刷,它们呈现出较高的平均 ROC-AUC,并且在一些纹理类别上也取得了不错的结果,如网格和皮革。
异常定位的每像素水平 ROC-AUC 得分[1]。
- 此表显示了 MVTec AD 数据集类别的异常定位结果。
- 与其他最先进的方法相比,它在异常定位方面获得了更高的总体 ROC-AUC 分数。
参考
[1] 视觉异常检测修复重建,Vitjan Zavrtanik,Matej Kristan,Danijel Skocaj,(2021)
[2] 通过将结构相似性应用于自动编码器来改进无监督缺陷分割,保罗·博格曼,辛迪·洛,迈克尔·福瑟,大卫·萨特勒格,卡斯滕·斯泰格
[3] 图像质量评估技术显示了自动编码器生成对抗网络的改进的训练和评估,迈克尔·o·弗托利,吉姆·戴维斯,卡尔顿大学,(2017)
[4] U-Net:用于生物医学图像分割的卷积网络,Olaf Ronneberger,Philipp Fischer,Thomas Brox,(2015)
[5]MVTec 异常检测数据集:用于无监督异常检测的综合真实数据集,保罗·博格曼、基利安·巴茨纳、迈克尔·福瑟(2021)
Github 知识库
其他相关文章:
你喜欢我的文章吗? 成为会员 每天无限获取数据科学新帖!这是一种间接的支持我的方式,不会给你带来任何额外的费用。如果您已经是会员, 订阅 每当我发布新的数据科学和 python 指南时,您都可以收到电子邮件!
论文简化:反相关噪声注入用于改进推广
机器学习研究
了解在梯度更新期间添加噪声如何有助于概化,包括在 TensorFlow 中的实现
在本文中,我们将查看安东尼奥·奥维多、汉斯·克斯汀、弗兰克·普罗斯克、弗朗西斯·巴赫和奥雷连·卢奇于 2022 年 2 月 6 日发表的一篇论文。它被称为反相关噪声注入,用于改进泛化【1】,而你可以在这里找到。
我会这样总结这篇论文:
当使用梯度下降优化复杂函数时(即,当训练神经网络时),在梯度更新期间以智能方式添加噪声。
让我们深入研究一下这张纸,看看与不太聪明的方法相比,聪明的方法是什么。在摘要中,作者写了以下内容:
将人工噪声注入梯度下降(GD)通常被用来提高机器学习模型的性能。通常,在这种扰动梯度下降(PGD) 方法中使用 不相关的噪声。但是,不知道这是否是最佳的,也不知道其他类型的噪声是否可以提供更好的泛化性能。在本文中,我们放大了关联连续 PGD 步骤的扰动的问题。我们考虑了各种目标函数,发现带有反相关扰动的 GD(“反 PGD”)比 GD 和标准(不相关的)PGD** 更具普遍性。为了支持这些实验发现,我们还得出了一个理论分析,表明**反 PGD 运动到更宽的最小值,而广东和 PGD 仍然停留在次优区域甚至发散。反相关噪声和泛化之间的这种新联系为利用噪声来训练机器学习模型的新方法开辟了领域。
这真的让我开始思考。在梯度下降过程中添加噪声不仅有帮助。如果该噪声在梯度下降步骤中相关,则优化结果在某些方面甚至更好。这种方法被作者称为反 PGD 。
在这篇文章中,我不会向你解释所有的(令人兴奋!)数学细节。相反,我将为您提供一些实现和图片,以便理解本文的主旨。我还尽力创建了论文中提到的优化器的实现,但是要小心使用代码,因为我在这方面也不是专家。但是我很高兴得到任何反馈!
先决条件
为了理解**反 PGD(反扰动梯度下降)**是什么,让我们简单回顾一下 GD 和衍生算法如 SGD 和 PGD 是如何工作的。我在本文的先决条件部分解释了关于 GD 和 SGD 的重要内容
但是让我给你一个简略的版本。
梯度下降
让我们假设我们想要最小化一个函数 f ,其梯度表示为∇ f ( θ )。然后,GD 随机初始化模型的参数 θ ,然后用简单的更新规则**t15】θ←θ—α∇f(θ)进行迭代更新,直到收敛,即 θ 或f(θ)do α 是学习率。
对于凸函数,如果学习率足够小,GD 保证找到全局——也是唯一的局部——最小值。例如,对 L2 损失进行线性回归就是这种情况。对于非凸函数,GD 可能会陷入(许多)局部极小值之一。
图片由我提供。
随机梯度下降
SGD 也做同样的事情,但是它估计 ∇ f ( θ )而不是像 GD 那样精确地计算它。这有几个优点:
- 使用较少的 RAM ,因为我们不需要解析完整的数据集来计算梯度
- *更快的收敛 **(在历元数中)*因为在每个历元中关于 dataset_size / batch_size 的梯度更新发生
- 根据罗伯特·克莱恩伯格、李沅芷和杨远[2]的说法,与 GD 相比,它倾向于找到更好的局部极小值。
第三点肯定不是显而易见的,你应该查看这篇论文来更深入地探讨这个话题。基本上,从 GD 到 SGD 平滑我们想要优化的功能(假设从现在开始最小化):
图片由我提供。
扰动梯度下降
这里,我们稍微改变了更新规则:
图片由我提供。
其中ξₙ*(扰动 ) 来自一个居中的正态分布 N (0, σ ),并且是随机独立的。在梯度更新期间添加这种噪声可能有助于优化器摆脱局部最小值。然而,作者无法观察到使用 PGD 训练的模型实现了更好的泛化性能。尽管如此,通过给这个想法一点小小的改变,作者使它变得更加有用。*
反扰动梯度下降
更准确地说,作者认为拥有反相关扰动是有益的。他们伪造它们的方式再简单不过了:
图片由我提供。
这里——再一次——ξₙ独立于一个 N (0, σ 分布。这已经足以产生完美的反相关扰动,这意味着
图片来自他们的论文。
这里, I 只是单位矩阵。为了得到为什么这是真的直觉,想象一下ξₙ是一维的。然后我们可以忽略换位,可以计算
图片由我提供。
这同样适用于更高维度。不错!
然后作者推断这些扰动对损失函数 f = L 的最小化有以下影响:
从他们的报纸上。
注: 一个函数的 黑森 只是广义二阶导数。例如,如果有函数 f ( x , y ) = xy ,则给出的黑纱为
图片由我提供。
轨迹 Tr 就是主对角线的和——从左上到右下,即 0 + 6 xy 。
主要要点是:
我们不是试图最小化损失函数,而是试图最小化损失加上其二阶导数的大小,二阶导数充当正则化项。
这也与从 GD 切换到 SGD 如何平滑损失函数有关。
反 PGD 如何对抗广东和 PGD
好的,我们加入了一些粗麻布和一条现在用于正规化的痕迹。但是为什么呢?为什么有这个正则化子是有益的?我们将在本节中找到答案!
为了给出一个直觉,作者在所谓的加宽谷函数上测试他们的反 PGD
图片由我提供。
他们试图将其最小化。虽然最小化这个函数是微不足道的(设置 v=0 或所有 u = 0),但作者(还有我)认为,例如,我们在(0,0,…,0)处的最小值比在(0,100,100,…,100)处的最小值更好一些。要了解原因,请看他们论文的截图:
从他们的报纸上。
在左边,你可以看到一个函数图。原点(0,…,0)的最小值被平地包围,而远离原点的最小值则在越来越陡的山谷中。这是图表中的大图:
从他们的论文中,加上我的注解。
因此,在某种意义上,我们希望我们的优化器在原点找到最小值,因为它感觉更安全*。如果我们在原点稍微摆动一下最小值,我们仍然会有一点损失。然而,如果我们达到最小值(0,100,…,100),稍微改变v——例如从 0 到 0.1——会使损失飙升到 100 d ,维度 d 越高,损失越大。*
当然,反 PGD 在起源中找到了好的最小值。
GD 似乎找到了退而求其次的东西, **PGD 甚至走向被高山环绕的险峻极小**。😅
反 PGD 发现它是因为黑森的痕迹告诉它:
从他们的报纸上。
在哪里
图片由我提供。
如果我们将此作为正则项添加到 L 中,优化器将被迫寻找最小值(0,…,0),因为优化目标变为
图片由我提供。
而这个函数最小化正好是对于 u = (0,… 0)和 v = 0,没有别的。
作者声称,这不仅适用于加宽谷函数,而且更是一个一般概念。从他们的论文来看:
从他们的报纸上。
履行
你甚至可以在实践中尝试一下,因为据我所知,我实现了 SGD (让你开始了解如何编写优化器)、PGD 和反 PGD。如有错误,请写评论!**
**import tensorflow as tf
class SGD(tf.keras.optimizers.Optimizer):
def __init__(self, learning_rate=0.01, name="SGD", **kwargs):
super().__init__(name, **kwargs)
self._set_hyper("learning_rate", kwargs.get("lr", learning_rate))
@tf.function
def _resource_apply_dense(self, grad, var):
var_dtype = var.dtype.base_dtype
lr_t = self._decayed_lr(var_dtype) # learning rate decay
new_var = var - grad * lr_t
var.assign(new_var)
def get_config(self):
base_config = super().get_config()
return {
**base_config,
"learning_rate": self._serialize_hyperparameter("learning_rate"),
}
class PGD(tf.keras.optimizers.Optimizer):
def __init__(self, learning_rate=0.01, std=0.5, name="PGD", **kwargs):
super().__init__(name, **kwargs)
self._set_hyper("learning_rate", kwargs.get("lr", learning_rate))
self._set_hyper("std", std)
@tf.function
def _resource_apply_dense(self, grad, var):
var_dtype = var.dtype.base_dtype
lr_t = self._decayed_lr(var_dtype)
perturbation = tf.random.normal(var.shape)
new_var = var - grad * lr_t + self._get_hyper("std", dtype=tf.float32) * lr_t * perturbation
var.assign(new_var)
def get_config(self):
base_config = super().get_config()
return {
**base_config,
"learning_rate": self._serialize_hyperparameter("learning_rate"),
"std": self._serialize_hyperparameter("std")
}
class AntiPGD(tf.keras.optimizers.Optimizer):
def __init__(self, learning_rate=0.01, std=0.5, name="AntiPGD", **kwargs):
super().__init__(name, **kwargs)
self._set_hyper("learning_rate", kwargs.get("lr", learning_rate))
self._set_hyper("std", std)
def _create_slots(self, var_list):
for var in var_list:
self.add_slot(var, "previous_perturbation", initializer="random_normal")
@tf.function
def _resource_apply_dense(self, grad, var):
var_dtype = var.dtype.base_dtype
lr_t = self._decayed_lr(var_dtype)
previous_perturbation = self.get_slot(var, "previous_perturbation")
current_perturbation = tf.random.normal(var.shape)
perturbation_diff = current_perturbation - previous_perturbation
new_var = var - grad * lr_t + self._get_hyper("std", dtype=tf.float32) * lr_t * perturbation_diff
previous_perturbation.assign(current_perturbation)
var.assign(new_var)
def get_config(self):
base_config = super().get_config()
return {
**base_config,
"learning_rate": self._serialize_hyperparameter("learning_rate"),
"std": self._serialize_hyperparameter("std")
}**
结论
在他们的论文中,Antonio Orvieto、Hans Kersting、Frank Proske、Francis Bach 和 Aurelien Lucchi 已经表明,在梯度更新期间添加噪声可能有利于找到更好的最小值。在机器学习的设置中,这些是具有良好泛化能力的最小值。
然而,像 PGD 那样添加独立噪声的天真方法不足以找到这些最小值——它甚至适得其反**,而(S)GD 似乎是更好的选择。作者推荐反相关噪声,产生反 PGD 优化器。**
作者声称可能运行良好是因为:
平坦最小值=小 Hessian =良好的概括
但是还有很多研究要做。他们甚至说 SGD 之所以如此有效是因为同样的效果:
从他们的报纸上。
所以,似乎即使对于像 SGD 这样简单的算法,还是有那么多不清楚的地方。我们甚至还没有达到 RMSprop、Adam 或 Fadam 的水平。
图片由我提供。
我希望你今天学到了新的、有趣的、有用的东西。感谢阅读!
作为最后一点,如果你
- 想支持我多写点机器学习和
- 无论如何都要计划获得中等订阅量,
为什么不做 通过这个环节 ?这将对我帮助很大!😊
透明地说,给你的价格不变,但大约一半的订阅费直接归我。
非常感谢,如果你考虑支持我的话!
有问题就在LinkedIn上写我!
参考
[1]奥维多,a .,克尔斯汀,h .,普罗斯克,f .,巴赫,f .和卢奇,a .,2022。反相关噪声注入,以提高泛化能力。 arXiv 预印本 arXiv:2202.02831
[2] Kleinberg,b .,Li,y .和 Yuan,y . 2018 年 7 月。另一种观点:SGD 何时逃离局部极小值?。在机器学习国际会议(第 2698–2707 页)。PMLR。