VIM 教程 — 像专业人士一样编辑文本
原文:
towardsdatascience.com/vim-tutorial-edit-text-like-a-pro-ea13e45010f5
易于遵循的 Vim 文本编辑器教程
·发布于 Towards Data Science ·12 分钟阅读·2023 年 3 月 6 日
–
图片由 Pereanu Sebastian 在 Unsplash 上提供
Vim 是一个功能强大且高度可定制的文本编辑器,自 1991 年以来一直存在。它以其模式编辑界面而闻名——这意味着它允许用户在不同的模式之间切换,用于编辑、导航和选择文本。Vim 被程序员和其他需要快速高效方式编辑和操作文本文件的高级用户广泛使用。使用 Vim 的主要优势之一是其速度和效率。一旦你掌握了命令和界面,你可以用几个击键执行复杂的编辑任务。
在这篇文章中,我们将介绍 Vim 中最常用的命令。我在本节之后包含了一个速查表,供将来参考。速查表之后是命令的详细教程。我建议首先阅读详细教程部分,然后在未来使用时,你可以简单地参考速查表。
开始吧!
(除非另有说明,否则所有图片均由作者提供。)
[## 通过我的推荐链接加入 Medium - Farzad Mahmoodinobar
阅读 Farzad (以及 Medium 上其他作者) 的每一个故事。你的会员费直接支持 Farzad 和其他人……
medium.com](https://medium.com/@fmnobar/membership?source=post_page-----ea13e45010f5--------------------------------)
重要 Vim 命令概览表
下表总结了一些你需要了解的重要 Vim 命令。每个命令在下一节中有更详细的描述。
Vim 命令速查表
Vim 命令 — 详细教程与实现
在本节中,我们将详细介绍一些最常用的 Vim 命令。我们还将把每个命令应用于文本文件以查看结果。我们将使用命令行界面(CLI)来运行 Vim 命令(如果你需要复习 CLI,可以访问这个 CLI 教程)。如果你准备好了,打开 CLI,开始吧!
1. “vim"
— 创建文件
首先,我们将创建一个名为 demo.txt
的文件,以便可以在该文本文件中应用命令,如下所示:
vim demo.txt
这个命令使用 Vim 在 CLI 中创建一个文本文件。一旦运行此命令,你将在 CLI 中看到一个类似于这张图片的空白页面:
如你所见,文件是空的,我们只能在左下角看到文件名。让我们看看如何在文本文件中添加一些文本。
2. “i” — 插入
i
命令代表“insert”,用于进入 Vim 的插入模式。当你处于插入模式时,可以像在任何其他文本编辑器中一样输入和编辑文本。要使用此命令,只需在普通模式下按 i
。
继续按 i
,然后输入以下内容:
Topic: VIM Tutorial
Author: Farzad Nobar
If you find this helpful, follow me on Medium at: https://medium.com/@fmnobar
确保不要按其他任何键。以下是此时我 CLI 的样子:
我们可以在该图片中看到文本,并且在最底部我们可以看到我们处于 — INSERT —
模式。为了退出该模式,只需按下键盘上的 Escape 键(确保在进入下一步之前执行此操作)。
到目前为止,我们已经在文件中添加了这些文本,但尚未保存更改。接下来我们来看看这个问题。
3. “:w” — 写入
:w
命令代表“write”,用于在 Vim 中保存对文件的更改。要使用此命令,只需在普通模式下输入 :w
并按下回车键(或 Enter)。这将保存对文件所做的任何更改,以下是更改后我的 CLI 的样子:
专业提示: 请注意,“Write”命令是在
:
之后输入的。在 Vim 中,我们通过输入命令:
进入“命令”模式。换句话说,我们首先通过输入:
进入命令模式,然后输入w
,这就成为了“Write”命令。我们可以随时通过按下 Escape 键退出命令模式。我们将在接下来的部分中更多地使用命令模式。
现在我们已经保存了更改,接下来我们可以退出(即关闭)文件。
4. ":q"
— 退出
:q
命令代表“quit”,用于退出 Vim。要使用此命令,只需在普通模式下输入 :q
(然后按下回车/Enter)。如果文件中有未保存的更改,Vim 会提示你在退出之前保存这些更改。
5. “:wq” — 写入并退出
:wq
命令代表“写入并退出”,用于将更改保存到文件并退出 Vim(而不是像之前的示例那样分开进行)。要使用此命令,在普通模式下键入:wq
。这将保存你对文件所做的任何更改并退出 Vim。
6. “h”、“j”、“k”、“l”、“w”、“b” — 文本文件中的导航
这些命令用于在 Vim 中移动光标。h
将光标向左移动,j
向下移动,k
向上移动,l
向右移动。w
将光标移动到下一个单词的开头,b
将光标移动到当前单词的开头。这些命令在普通模式下使用。
为了练习这些,请使用以下命令打开我们创建的文件:
vim demo.txt
现在你已经在 Vim 中打开了文件,你可以使用导航键进行移动——试试看吧!
技巧提示: 请注意,打开现有文件和创建新文件的命令是相同的。如果文件已存在,Vim 将打开现有文件。如果文件不存在,Vim 将创建一个新文件。
7. “x” — 删除
x
命令用于删除光标下的字符。要使用此命令,只需将光标放在你要删除的字符上,并在普通模式下按x
即可。
让我们用这个来删除“Topic”中的“ic”字母。我看到的结果如下:
但如果我们不小心删除了这些呢?让我们看看下一步如何撤销这个操作。
8. “u” — 撤销
u
命令用于在 Vim 中撤销更改。要使用此命令,只需在普通模式下按u
即可。
我按了u
两次,删除操作被撤销,如下所示:
9. “Ctrl-r” — 重做
Ctrl-r
命令用于在 Vim 中重做更改。要使用此命令,只需在普通模式下按Ctrl-r
即可。
假设我们又改变了主意,实际上我们是要删除那两个字母。我按了Ctrl-r
两次,在下面的截图中我们可以看到字母再次被删除:
好的。我们先暂时保留这些更改。接下来我们来谈谈如何使用 Vim 搜索术语。
10. “dd” — 删除行
dd
命令用于在 Vim 中删除整行。要使用此命令,只需在普通模式下按dd
即可。
11. “/search_term” — 搜索
/
命令用于在 Vim 中搜索术语。要使用此命令,键入/
后跟你要搜索的术语,然后按回车。Vim 将突出显示文件中术语的下一个出现位置。
试试看吧。我使用/medium
搜索了“medium”,结果如下:
正如我们在图片中看到的,光标跳到了文本最后一行的“medium”一词的开头。
现在让我们使用 :set hlsearch
高亮显示搜索结果,现在搜索的词汇(例如本例中的“medium”)被高亮显示,如下图所示:
我们可以使用 :set nohlsearch
关闭此高亮搜索。
12. “😒/search_term/replace_term/g” — 替换
:s/search_term/replace_term/g
命令用于在 Vim 中替换所有出现的词汇。要使用此命令,在普通模式下键入 :s/search_term/replace_term/g
并按回车键。Vim 将用替换词汇替换所有出现的搜索词汇。
我通过使用以下命令将“Tutorial”替换为“guideline”进行尝试::s/Tutorial/Guideline/g
以下是结果:
接下来,让我们讨论一下如何完成文本复制并粘贴的操作。
13. “yy” — 剪切
yy
命令用于在 Vim 中复制一行文本。要使用此命令,只需将光标放在要复制的行上,并在普通模式下按 yy
。
14. “p” — 粘贴
p
命令用于粘贴已被剪切或删除的文本。要使用此命令,只需在普通模式下按 p
键即可。
让我们尝试剪切和粘贴命令。首先在文件的开头按 yy
,然后按 p
,看看会发生什么。请注意,按下 yy
后实际上没有明显的变化,但实际情况是在后台进行的。一旦按下 p
,你将看到结果。我碰巧按了两次 p
,以下是结果:
15. ":set number"
— 显示行号
:set number
命令用于在 Vim 中显示行号。要使用此命令,在普通模式下键入 :set number
。
这个命令不言而喻。以下是我输入命令后 CLI 的样子:
16. “:set nonumber” — 隐藏行号
:set nonumber
命令用于在 Vim 中隐藏行号。要使用此命令,在普通模式下键入 :set nonumber
。
以下是隐藏行号的结果:
17. “:set paste” — 无自动缩进的粘贴
:set paste
命令用于粘贴文本而不进行自动缩进。要使用此命令,在普通模式下键入 :set paste
,然后粘贴文本,最后键入 :set nopaste
退出粘贴模式。此命令的一个应用场景是粘贴对缩进敏感的文本输入,例如编程脚本(例如 Python 中的 for 循环等)。
18. “:set spell” — 拼写检查
:set spell
命令用于启用 Vim 的拼写检查。要使用此命令,在普通模式下键入 :set spell
。Vim 会高亮显示任何拼写错误的单词。
让我们看看 Vim 是否在我们的文件中识别出了什么:
正如预期的那样,我的名字被突出显示,Vim 未将其识别为正确拼写的词——我不会把这个放在心上!
19. “:set nospell” — 禁用拼写检查
:set nospell
命令用于在 Vim 中禁用拼写检查。要使用此命令,请在正常模式下输入:set nospell
。
20. “:w filename” — 写入文件
:w filename
命令用于将当前文件的内容写入一个具有指定文件名的新文件。要使用此命令,请在正常模式下输入:w filename
。
试试这个。我将首先使用以下命令将文件内容保存到名为demo2.txt
的新文件中::w demo2.txt
。然后我关闭了 Vim(使用:q!
),接着查看文件(使用 CLI 中的ls
命令),看到新文件demo2.txt
现在被添加到了那里。让我们用 Vim 打开它,看看它的样子:
21. “:q!” — 不保存退出
:q!
命令用于退出 Vim 而不保存对文件的任何更改。要使用此命令,请在正常模式下输入:q!
。如果你在之前的例子中注意到,我使用:q!
退出了 Vim,因此我们期望更改没有被保存。让我们打开demo.txt
并进行验证。结果如下:
正如预期的那样,修改没有被保存,我最终得到了原始文件。
22. “:e filename” — 打开一个文件
:e filename
命令用于在 Vim 中打开一个文件。要使用此命令,请在正常模式下输入:e filename
。
现在我们已经打开了demo.txt
文件,接下来使用以下命令打开第二个名为demo2.txt
的文件,结果如下:
正如预期的那样,第二个文件已被打开。
23. “:set tabstop=4” — 设置制表符宽度
:set tabstop=4
命令用于将 Vim 中的制表符宽度设置为 4 个空格。要使用此命令,请在正常模式下输入:set tabstop=4
。
试试这个。我将首先将 tabstop 设置为 4,进入插入模式,然后在文件第二行的开头添加一个制表符,结果如下:
现在让我们将 tabstop 大小更改为 8,看看它如何变化:
请注意,现有的制表符在文件中自动应用了更改,制表符大小从 4 增加到 8,如上图所示。
24. “:set expandtab” — 使用空格代替制表符
:set expandtab
命令用于在 Vim 中将制表符替换为空格。要使用此命令,请在正常模式下输入:set expandtab
。这个命令一目了然,我们可以继续下一个命令。
25. “:set syntax=python” — 语法高亮
:set syntax=python
命令用于在 Vim 中启用 Python 代码的语法高亮显示。要使用此命令,请在正常模式下输入:set syntax=python
。
我很想包括这一点,因为它与我的工作有关,并且我每天都在使用 Python。为了测试这一点,让我们采取以下步骤:
1. 使用 Vim 打开一个新的 Python 文件,方法是:vim demo.py
2. 粘贴以下命令(从我关于多变量分析的帖子中选择)
# Import libraries
import numpy as np
import pandas as pd
import seaborn as sns
from scipy import stats
import matplotlib.pyplot as plt
%matplotlib inline
# Show all columns/rows of the dataframe
pd.set_option("display.max_columns", None)
pd.set_option("display.max_rows", None)
# Read the data
df = pd.read_csv('auto-cleaned.csv')
# Return top 5 rows of the dataframe
df.head()
以下是我看到的结果:
3. 然后我通过使用:set syntax=python
将语法设置为 Python
4. 然后我通过使用:syntax on
来开启语法高亮
以下是结果,它符合预期的 Python 语法:
正如预期的那样,Python 语法得到了识别和颜色编码。
结论
在这篇文章中,我们介绍了 Vim,这是一个在命令行界面(CLI)中使用的多功能文本编辑器,被程序员和高级用户所利用。然后我们逐步讲解了一些在 Vim 中最常用的命令及其示例。我希望通过本教程,你对使用 Vim 感到更加舒适,并开始将其整合到你日常的文本编辑活动中。
感谢阅读!
如果你觉得这篇文章对你有帮助,请在 Medium 上关注我并订阅以接收我最新的帖子!
视觉基础的重复计数在实际应用中的探索
对视觉基础重复计数的不同方法进行回顾
·
关注 发表在 Towards Data Science ·9 分钟阅读·2023 年 2 月 21 日
–
src: 图片由 @paragdmehta 提供,展示了一个重复的模式。
在这篇文章中,我尝试解释我对不同视觉基础的重复计数技术的探索,并讨论它们的优缺点。具体而言,我重点介绍了计算机视觉在重复计数中应用的五种主要方式。
穿戴式传感器在重复和组计数中非常受欢迎。由于这些传感器价格昂贵,而且在大多数情况下仅限于跟踪特定的身体部位,近年来,越来越多的关注转向了使用视觉基础的方法进行重复计数。
从活动监控、运动和游戏中的无数应用,到帮助了解生物事件(心跳、脉搏计数等)发生的次数,重复计数是学术界和工业界积极解决的问题。
关键词: 重复计数、计算机视觉、姿态估计。
目录
-
RepNet: 无类别的重复计数应用
-
基于规则的姿态估计运动重复计数
-
基于信号处理的运动重复计数
-
GymCam
-
基于深度学习的光流方法进行重复计数
我们在博客中讨论的大多数技术不会是通用的,而是专门针对特定问题(例如锻炼)。此外,若要深入了解该技术,请参阅提供的参考文献。
1. RepNet: 无类别的重复计数应用
论文: Counting Out Time: 无类别的视频重复计数 [1]
图:RepNet 体系结构。源:arxiv.org/pdf/2006.15418.pdf
在重复计数方面最突出的工作之一是 RepNet,这是一个端到端的深度学习模型,可以准确预测广泛的重复运动计数。
RepNet 模型接收视频流作为输入,并预测两个输出:
每帧周期长度: 对于每一个重复动作的帧,我们希望知道该动作的周期长度(以时间单位表示)。
每帧周期性: 表示当前帧是否为重复的一部分的分数。
RepNet 模型的几个关键亮点包括:时间自相似矩阵(TSM):
图:RepNet 利用时间自相似矩阵(TSM)。源:arxiv.org/pdf/2006.15418.pdf
-
TSM 是这种重复计数技术的亮点。它是 RepNet 体系结构的信息瓶颈。该矩阵通过计算两个嵌入之间的成对相似性函数来帮助将帧彼此关联起来。
-
还可以通过(使用启发式方法)从这些 TSM 推断重复次数,这使得 RepNet 模型的预测具有解释性。
-
多样化的真实世界重复视频确保这些 TSM 足够多样化,因此 RepNet 除了重复计数之外还有一系列应用。
这种重复计数方法最令人印象深刻的一点是它是无类别的(通用的),适用于广泛的重复动作。RepNet 模型是流行的 Transformers 在计算机视觉中应用的经典实例。
源:arxiv.org/pdf/2006.15418.pdf
然而,该模型存在一定限制,因为输入视频中的帧数必须有限。这可以归因于 TSM 的大小等于输入帧的数量。
该模型相当庞大且复杂,因此在移动应用程序或任何生产环境中部署这一模型将是相当具有挑战性的,并可能存在延迟问题。
2. 基于规则的姿态估计重复计数
博客: 使用计算机视觉赢得互动 [2]
这是行业中最常见的想法。许多健康和健身初创公司一直在努力构建准确、轻量化的最先进姿态估计模型,这些模型可以用来准确计算运动中的重复次数,并提供姿势纠正反馈等。
涉及的主要步骤:
-
给定一个特定的练习,你首先需要为该练习中的状态制定定义(规则)。一个练习中可以有多个状态。例如,深蹲练习可以分为两个状态,即下半部状态和上半部状态。在运动过程中,做练习的人将从一个状态转移到另一个状态。这些状态规则可以看作是运动过程中激活区域的表示。
-
例如,对于深蹲,这些规则可以是(th指阈值):
down: (left_knee_hip_dist_y < th1 and right_knee_hip_dist_y < th2)
up: (left_knee_hip_dist_y > th3 and right_knee_hip_dist_y > th4)
- 在推理过程中,我们首先实时计算模型中姿态关键点的度量(角度、归一化的距离),并检查某个特定规则是否被激活,然后使用标志进行重复计数。
来源:使用计算机视觉赢得互动。图片由作者提供。
该方法的主要优点之一是重复计数快速且准确,延迟非常低。然而,一些主要的缺点包括:
-
这不是通用的重复计数。
-
姿态估计模型对背景噪音非常敏感,因此对重复计数也有影响。
-
可扩展性问题: 手动编写规则是一个耗时的过程。我们还需要测试不同角度、方向、姿势等的规则变体。想象一下为语料库中的数百种练习编写规则。
3. 使用信号处理思想进行重复计数
博客: 使用信号处理思想构建运动重复计数器 [3]
目标: 使用零交叉和峰值检测等信号处理思想来制作一个运动重复计数器。
这种方法与基于规则的重复计数非常相似,只是需要手动编写不同状态的规则。这种方法通过推断参考线(可以视为状态边界)来半自动化状态计算方法,使用训练师的视频来进行特定运动/练习,然后使用该参考线来计数任何视频中的该练习的重复次数。
在这里,我们将运动视为关键点度量的波形集合。这些度量包括不同身体关键点之间的角度和距离,关键点是通过姿态估计模型(Tensorflow 的 Movenet 姿态估计模型)计算的。
主要步骤:
-
我们首先使用训练师参考视频(作为输入)计算关键点之间的度量(距离和角度)。这些度量在时间上表示为信号。
-
我们过滤掉所有静态信号,并创建非静态信号的组合信号。然后,我们使用汇总信号的均值计算参考线。
-
在推断过程中,我们首先在测试用户输入视频上计算度量,并实时计算整体组合信号。
-
我们创建一个固定大小的移动窗口,并检查整体信号(来自 3)与参考线(来自 2)的交点。这个交点表明重复动作已经完成。
使用零交叉思想的结果。图片由作者提供。 源
这种方法快速、易于实施且相当准确。然而,主要的缺点包括以下几点:
-
重复计数是 独占性的 和 非通用的。
-
对背景噪音高度敏感。
-
缩放问题:需要使用参考视频计算零交叉线来进行任何活动(同时确保视频没有噪音)。
4. GymCam
论文: 在不受约束的场景中检测、识别和跟踪同时进行的运动[4]
GymCam 是一个基于视觉的系统,用于自动化的运动重复计数和跟踪。它基于这样的假设:任何在健身房内的重复运动都属于某种运动。在这里,系统的输入是来自摄像头的视频流,输出是多个与运动相关的度量,包括重复计数。
步骤总结
GymCam:主要步骤。图片由作者提供。
-
使用密集的 光流 算法检测视频中的所有潜在运动轨迹。 运动轨迹也可能是非运动活动的结果,例如热身、用户的步态、到处走动等。
-
检测所有 运动 轨迹在场景中。 他们是怎么做到的?首先,他们执行一个特征提取步骤,从任何轨迹的 5 秒窗口中提取手工特征。他们使用一个MLP 基于的二分类器模型,该模型输入特征并输出该输入轨迹(特征)是否与运动相关的概率。
-
聚类 运动轨迹在空间和时间上的练习。 聚类后,通过将所有属于给定聚类的轨迹合并生成一个平均运动轨迹。请注意,聚类的数量是预定义的。这些平均轨迹随后用于计数和跟踪练习的重复次数。
-
重复计数 和 运动识别: 平均轨迹然后被转换为特征向量,这些特征向量被输入到 MLP 回归模型和 MLP 分类模型中,以分别推断重复计数和运动标签。
从组合轨迹中进行重复计数和练习识别。图片由作者提供。
该系统的一些显著特点是:它是一个端到端 系统,可以在现实世界环境中执行重复计数。光流 识别所有运动,因此即使用户几乎不可见,也足以跟踪运动并执行重复计数。
系统存在的问题:
-
多个用户重叠 在视频中进行运动。因此,很难确定这些用户的确切边界并推断重复次数。
-
噪声敏感: 噪声人类行为,如热身、休息、用户的步态等,可能会表现出周期性,因此可能对重复计数产生不必要的贡献。
-
重复计数不是通用的:该系统仅限于运动重复计数。
5. 基于 DL 的光流方法
博客: 使用深度学习和光流算法的运动计数应用 [5]
另一个有趣的思路是利用视觉解决重复计数问题,即光流方法。
主要步骤
- 使用密集光流算法查找重复活动中视频帧的颜色编码表示。 这里的关键在于不同的重复运动状态将有不同的颜色编码。
密集光流将向下运动编码为绿色,将向上运动编码为紫色。Gif 由作者提供。
关于光流算法的详细信息,请参见opencv文档 这里(以及实现)。
2. 数据集创建:下一步是生成一个带有颜色编码的图像和视频数据集,并将其标注为不同的运动状态(如向上或向下)。
3. 模型训练:下一步涉及训练一个普通的 CNN 模型,以对帧进行多类分类。在测试时,将来自光流的颜色编码帧输入到模型中,模型预测运动状态之一并捕获类别标签。这基本上是一个通过模型进行的颜色匹配问题,因为模型更为健壮。
该方法准确且容易在生产环境中部署。然而,缺点往往超过优点:
-
重复计数是 独占性 和 类别依赖性 的。
-
扩展问题:每次将新运动添加到语料库时,都需要标注数据集并训练模型。
-
方向敏感性:相同的动作在不同的方向上会有不同的颜色编码,从而导致错误的模型预测。这是该方法的主要限制之一。
-
噪声敏感性:背景中的任何轻微噪声都会改变这些颜色编码,从而影响模型的预测。
参考文献
[1]. Dwibedi, Debidatta 和 Aytar, Yusuf 和 Tompson, Jonathan 和 Sermanet, Pierre 和 Zisserman, Andrew. 计时:在实际环境中进行类无关的视频重复计数。IEEE/CVF 计算机视觉与模式识别会议(CVPR)。DOI: doi.org/10.48550/arxiv.1902.09868
[2]. Aakash Agrawal. 利用计算机视觉赢得交互性。 The .fit 方式。
[3]. Aakash Agrawal. 利用信号处理中的想法构建一个运动重复计数器。Towards Data Science。
[4]. Rushil Khurana, Karan Ahuja, Zac Yu, Jennifer Mankoff, Chris Harrison, 和 Mayank Goel. 2019. GymCam:在不受约束的场景中检测、识别和跟踪同时进行的运动。Proc. ACM Interact. Mob. Wearable Ubiquitous Technol. 2, 4, Article 185. DOI: doi.org/10.1145/3287063
[5]. Art Kulakov. 我如何使用深度学习和光流算法创建健身动作计数应用程序。Towards Data Science。
我希望你喜欢探索一些基于视觉的重复计数技术。大多数想法都很容易实现和部署。我希望了解任何阅读这篇博客的人的反馈。我很乐意回答有关上述任何概念的疑问/问题。欢迎反馈。你可以通过 Linkedin 联系我。
感谢阅读!
面向视觉的语义占用预测用于自动驾驶
关于 2023 年上半年学术“占用网络”的综述
·发布在Towards Data Science ·阅读时间 11 分钟·2023 年 5 月 29 日
–
现有的 3D 物体检测方法在自动驾驶中的一个关键痛点是它们通常输出简洁的 3D 边界框,忽略了更精细的几何细节,并且在处理一般的、超出词汇表的物体时表现不佳。这一痛点存在于单目 3D 物体检测和 BEV 多摄像头物体检测中。为了解决这个问题,占用网络作为一种面向视觉的通用障碍检测解决方案,首次在特斯拉在 CVPR 2022 的主题演讲中介绍,并在AI Day 2022中推广。更多细节请参见之前关于可行驶空间的博客系列。
截至 2023 年的行业应用最新趋势
medium.com](https://medium.com/@patrickllgc/drivable-space-in-autonomous-driving-the-industry-7a4624b94d41?source=post_page-----16a46dbd6f65--------------------------------)
在学术界,与占用网络相对应的感知轨道被称为 语义占用预测(SOP),有时也称为 语义场景完成(SSC),两者之间有一些细微的区别。语义占用预测为场景中的每个体素分配占用状态和语义标签。这是一种通用且足够表达的表示方式,可以描述已知类别但形状不规则或不在已知白名单中的物体。本文将回顾截至 2023 年初的语义占用预测的最新方法。这个领域在学术界受到广泛关注,顶级会议上提交了大量论文,今年的 CVPR 也有一个占用预测挑战。
语义场景完成 vs 语义占用预测
语义场景完成(SSC)的概念最初在 SSCNet 论文(CVPR 2017)中提出,后来由 SemanticKITTI(ICCV 2019)普及,该数据集提供了官方数据集和比赛赛道。最近,出现了一种略有不同的任务语义占用预测(SOP)。SSC 和 SOP 都旨在预测给定空间位置处体素的占用状态和语义类别,但存在一些细微差别。首先,SSC 的输入模式是由 LiDAR 或其他主动深度传感器收集的部分 3D 数据,因此称为 3D 语义场景的“完成”。SOP 使用 2D 图像,可能是多摄像头和多帧的。除此之外,SSC 通常关注静态场景,而 SOP 也可以处理动态物体。总之,SOP 似乎是更通用和更受欢迎的术语,在本文中,我们将交替使用语义占用预测、语义场景完成和占用网络。
MonoScene 的开创性工作首次使用单目图像在 SemanticKITTI 上进行语义占用预测任务。它仍然将任务称为 SSC,这可能是因为 SemanticKITTI 主要包含静态场景。后续研究更倾向于使用 SOP 这一术语,将任务扩展到 NuScenes 和 Waymo 等其他数据集,并处理动态物体。
开创性工作的高级总结
我将首先总结过去一年研究的爆炸性增长,然后跟进各种技术细节的总结。下面是一个总结待审阅工作的整体发展脉络的图示。值得注意的是,该领域仍在快速发展,并且尚未趋向于一个普遍接受的数据集和评估指标。
语义占用预测领域的发展时间线(来源:作者创作)
MonoScene(CVPR 2022),首次尝试视觉输入
MonoScene是首个仅使用RGB 图像作为输入来重建户外场景的工作,相较于以往研究中使用的激光雷达点云。它是一个单摄像头解决方案,专注于前摄像头的 SemanticKITTI 数据集。
MonoScene 的架构(来源:MonoScene)
论文提出了许多想法,但似乎只有一个设计选择至关重要——FLoSP(Feature Line of Sight Projection)。这个想法类似于沿视线传播特征的思想,也被OFT(BMVC 2019)或Lift-Splat-Shoot(ECCV 2020)所采用。其他新颖性如 Context Relation Prior 和直接优化度量的独特损失,根据消融研究看来似乎不那么有用。
VoxFormer (CVPR 2023),显著改进了 monoScene
VoxFormer的关键见解在于 SOP/SSC 必须同时解决两个问题:可见区域的场景重建和遮挡区域的场景幻觉。VoxFormer 提出了一种重建和密集化的方法。在第一次重建阶段,论文利用单目深度方法将 RGB 像素提升为伪 LiDAR 点云,然后将其体素化为初始查询提案。在第二次密集化阶段,这些稀疏的查询通过图像特征增强,并利用自注意力进行标签传播以生成密集预测。VoxFormer 在 SemanticKITTI 上的性能显著优于 MonoScene,且仍为单摄像头解决方案。图像特征增强架构大量借鉴了BEVFormer的变形注意力思想。
VoxFormer 的架构(来源:VoxFormer)
TPVFormer (CVPR 2023),首次多摄像头尝试
TPVFormer是首个将 3D 语义占用预测推广到多摄像头设置的工作,并将 SOP/SSC 的理念从 SemanticKITTI 扩展到 NuScenes。
TPVFormer 的架构(来源:TPVFormer)
TPVFormer 将 BEV 的理念扩展到三个正交轴。这允许在不压制任何轴的情况下建模 3D,避免了立方复杂度。具体来说,TPVFormer 提出了两个步骤的注意力来生成 TPV 特征。首先,它使用图像交叉注意力(ICA)来获取 TPV 特征。这本质上借鉴了BEVFormer的思想,并扩展到其他两个正交方向以形成 TriPlane View 特征。然后,它使用交叉视图混合注意力(CVHA)通过关注其他两个方向来增强每个 TPV 特征。
预测比 TPVFormer 中的监督更密集,但仍然存在间隙和孔洞(来源:TPVFormer)
TPVFormer 使用了来自普通 NuScenes 数据集的稀疏激光雷达点进行监督,没有任何多帧密集化或重建。尽管在训练时进行的监督是稀疏的,但它声称模型能够在推理时预测所有体素的更密集和一致的体积占据。然而,这种密集的预测仍然不如后来的研究,如使用密集 NuScenes 数据集的 SurroundOcc 那样密集。
SurroundOcc(Arxiv 2023/03)和 OpenOccupancy(Arxiv 2023/03),是首次尝试密集标签监督
SurroundOcc认为密集预测需要密集标签。论文成功证明了更密集的标签可以显著提高之前方法(如 TPVFormer)的性能,提升近 3 倍。其最大贡献是生成密集占据真实数据的管道,无需昂贵的人为标注。
SurroundOcc 的 GT 生成管道(来源:SurroundOcc)
密集占据标签的生成涉及两个步骤:多帧数据汇总和密集化。首先,将动态物体和静态场景的多帧激光雷达点分别拼接在一起。累计的数据比单帧测量更密集,但仍然存在许多孔洞,需要进一步密集化。密集化是通过三角网格的泊松表面重建和最近邻(NN)将标签传播到新填充的体素上来完成的。
OpenOccupancy与 SurroundOcc 同时期且在精神上相似。与 SurroundOcc 一样,OpenOccupancy 也使用了一个管道,该管道首先分别汇总动态物体和静态场景的多帧激光雷达测量数据。为了进一步密集化,OpenOccupancy 采用了Augment-and-Purify(AAP)方法,而不是SurroundOcc所采用的泊松重建方法。具体来说,一个基线模型使用汇总的原始标签进行训练,然后用其预测结果与原始标签融合以生成更密集的标签(即“增强”)。这种更密集的标签大约密集 2 倍,并由人工标注者进行手动精炼(即“净化”)。总共投入了 4000 小时的人工时间来精炼 nuScenes 的标签,大约每 20 秒的片段需要 4 小时的人工时间。
SurroundOcc 的架构(来源:SurroundOcc)
CONet 的架构(来源:OpenOccupancy)
与密集标签生成管道的贡献相比,SurroundOcc 和 OpenOccupancy 的网络架构不如创新。SurroundOcc 主要基于BEVFormer,通过粗到细的步骤来增强 3D 特征。OpenOccupancy 提出了 CONet(级联占用网络),其方法类似于Lift-Splat-Shoot,将 2D 特征提升到 3D,然后通过级联方案增强 3D 特征。
Occ3D(Arxiv 2023/04),首次尝试遮挡推理
Occ3D还提出了一种生成密集占用标签的管道,包括点云聚合、点标注和遮挡处理。这是首篇明确处理密集标签的可见性和遮挡推理的论文。可见性和遮挡推理对于 SOP 模型的车载部署至关重要。在训练过程中,必须对遮挡和可见性进行特殊处理,以避免对不可观察场景的过度幻想导致假阳性。
值得注意的是,激光雷达的可见性与相机的可见性不同。激光雷达的可见性描述了完整性,因为有些体素在多帧数据聚合后仍不可观察。这在整个序列中是一致的。同时,相机的可见性关注于检测的可能性,而且在每个时间戳上不同。评估只在激光雷达和相机视图中的“可见”体素上进行。
在密集标签的准备过程中,Occ3D 仅依赖于多帧数据聚合,没有像 SurroundOcc 和 OpenOccupancy 那样的第二阶段密集化。作者声称,对于 Waymo 数据集,标签在没有密集化的情况下已经相当密集。对于 nuScenes,尽管点云聚合后标注仍有孔洞,但 Poisson 重建导致结果不准确,因此没有执行密集化步骤。也许 OpenOccupancy 的 Augment-and-Purify 方法在这种情况下更为实用。
CTF-Occ 在 Occ3D 中的架构(来源:Occ3D)
Occ3D 还提出了一种神经网络架构:粗到细占用(CTF-Occ)。粗到细的思想与 OpenOccupancy 和 SurroundOcc 中的基本相同。CTF-Occ 提出了增量标记选择以减少计算负担。它还提出了一种隐式解码器,用于输出任何给定点的语义标签,类似于占用网络的思想。
技术细节比较
上述关于语义占用预测的研究总结在下表中,包括网络架构、训练损失、评估指标以及检测范围和分辨率。
最近的语义占用预测论文技术细节的比较截至 2023 年 4 月(来源:作者创建,文本版)
网络架构
大多数研究基于已验证的 BEV 感知最先进方法,如BEVFormer和Lift, Splat, Shoot。架构主要分为两个阶段:2D 到 3D 特征提升和 3D 特征增强。有关更详细的总结,请参见上表。架构似乎已基本趋于一致。最重要的是密集占用注释生成管道,以及训练过程中的密集监督。
以下是SurroundOcc、OpenOccupancy和Occ3D中生成密集占用标签的自动标记管道的总结。
SurroundOcc、OpenOccupancy和Occ3D中密集标签管道的总结(来源:作者创建)
训练损失
语义占用预测任务与语义分割非常相似,SOP 需要对 3D 空间中的每个体素预测一个语义标签,而语义分割需要对每个测量点(无论是图像中的像素,还是激光雷达扫描中的 3D 点)进行预测。语义分割的主要损失函数是交叉熵损失和Lovasz 损失。Lovasz 扩展使得神经网络可以直接优化平均交集-并集(IoU)指标。
可能受到 Lovasz 的启发,monoScene 提出了几种其他损失函数,可以直接优化评估指标。然而,它们似乎较为深奥,并未完全通过消融研究得到支持。
评估指标
主要指标是几何占用预测的 IoU(一个体素是否被占用)和语义分类的 mIoU(平均 IoU)。这些指标可能不适合工业应用。
基于视觉的 SOP 任务需要成熟以满足工业使用并取代激光雷达。虽然 IoU 指标中计算了精度和召回率,但精度在 ADAS(高级驾驶员辅助系统)应用中始终更为重要,以避免虚假刹车,只要我们还有司机在驾驶。
检测范围和分辨率
目前所有跟踪算法在自车周围预测 50 米的范围。体素分辨率从 SemanticKITTI 的 0.2 米到 NuScenes 和 Waymo 数据集的 0.4 米或 0.5 米不等。这是一个良好的起点,但可能仍然不适用于工业应用。
一个更合理的分辨率和范围可能是 50 米范围内的 0.2 米,以及 50 米到 100 米范围内的 0.4 米。
相关任务
有两个与 SOP 相关的任务,即周围深度图和激光雷达语义分割,我们将在下面简要回顾。
周围深度图预测任务(例如 FSM 和 SurroundDepth)扩展了单目深度预测,并利用重叠摄像头视场中的一致性来进一步提升性能。它更多关注测量源,通过为图像中的每个像素赋予深度值(自下而上),而 SOP 则更多关注 BEV 空间中的应用目标(自上而下)。Lift-Splat-Shoot 与 BEVFormer 在 BEV 感知中的类似情况也是如此,前者是自下而上的方法,后者是自上而下的。
激光雷达语义分割专注于为激光雷达扫描中的每个点云分配语义类别标签。现实世界中的 3D 感知本质上是稀疏和不完整的。为了全面的语义理解,单纯解析稀疏测量而忽略未观察到的场景结构是不够的。
主要收获
-
语义占用预测中的神经网络架构似乎已经基本收敛。最重要的是自动标签管道,以生成密集的占用标签和训练过程中的密集监督。
-
当前常用数据集采用的检测范围和体素分辨率对于工业应用来说是不够的。我们需要更大的检测范围(例如 100 米)和更细的分辨率(例如 0.2 米)。
-
目前的评估指标对于工业应用也显得不够。对于 ADAS 应用,精确度比召回率更为重要,以避免频繁的虚假刹车。
-
语义占用预测的未来方向可能包括场景流估计。这将有助于预测未知障碍物的未来轨迹,并在自车轨迹规划过程中进行碰撞避免。
注:本博客文章中的所有图片均由作者创作,或来自公开的学术论文。详情请参见图注。
参考文献
-
SSCNet: 从单幅深度图像进行语义场景补全,CVPR 2017
-
SemanticKITTI: 用于激光雷达序列语义场景理解的数据集,ICCV 2019
-
MonoScene: 单目 3D 语义场景补全,CVPR 2022
-
VoxFormer: 基于相机的稀疏体素变换器用于 3D 语义场景补全,CVPR 2023
-
TPVFormer: 三视角视图用于基于视觉的 3D 语义占用预测,CVPR 2023
-
Occ3D: 自主驾驶的大规模 3D 占用预测基准,Arxiv 2023/04
-
SurroundOcc: 自主驾驶的多摄像头 3D 占用预测,Arxiv 2023/03
-
OpenOccupancy: 周围语义占用感知的大规模基准,Arxiv 2023/03
-
SimpleOccupancy: 自主驾驶中 3D 占用估计的简单尝试,Arxiv 2023/03
-
OccFormer: 基于视觉的 3D 语义占用预测的双路径变换器,Arxiv 2023/04
-
BEVFormer: 通过时空变换器从多摄像头图像学习鸟瞰视图表示,ECCV 2022
-
FSM: 来自多摄像头的完整周围单目深度,ICRA 2021
-
SurroundDepth: 通过周围视图纠缠进行自监督多摄像头深度估计,CoRL 2022
-
Lift, Splat, Shoot (LSS): 通过隐式反投影到 3D 来编码来自任意相机设备的图像,ECCV 2020
-
OFT: 单目 3D 物体检测的正射特征变换,BMVC 2019
使用冻结的大型语言模型进行视觉问答
原文:
towardsdatascience.com/visual-question-answering-with-frozen-large-language-models-353d42791054
与大型语言模型讨论图像,而无需在图像上训练大型语言模型。
·发表于 Towards Data Science ·18 分钟阅读·2023 年 10 月 9 日
–
“弥合模态”,由 MidJourney 制作。除非另有说明,否则所有图像均由作者提供。
在这篇文章中,我们将使用 Q-Former,一种弥合计算机视觉和自然语言模型的技术,来创建一个视觉问答系统。我们将讨论必要的理论,参照 BLIP-2 论文,然后实现一个可以与大型语言模型讨论图像的系统。
我们将构建的内容
谁会觉得这篇文章有用? 对计算机视觉、自然语言处理和多模态建模感兴趣的数据科学家。
这篇文章的难度如何? 中级。如果你没有一些计算机视觉和自然语言处理的经验,你可能会觉得有些困难。
前提条件: 对变换器、嵌入和编码器-解码器有较高的熟悉度。所有这些主题在以下文章中都有涵盖:
Transformers — 直观且全面的解释
探索现代机器学习的浪潮:一步一步拆解变换器
towardsdatascience.com](/transformers-intuitively-and-exhaustively-explained-58a5c5df8dbb?source=post_page-----353d42791054--------------------------------)
视觉语言建模的简要时间线
视觉语言建模真正起步于 2016 年,随着论文 VQA: Visual Question Answering 的发布,该论文正式提出了以下问题类别:
给定一张图片和关于这张图片的自然语言问题,任务是提供一个准确的自然语言回答——VQA: 视觉问答
在 2016 年,当 VQA(视觉问答)被普及时,典型的方法看起来是这样的:
2016 年的一个 VQA 模型使用 LSTM 将问题嵌入向量,使用现有的计算机视觉网络将图像嵌入向量,然后通过一个密集层在正确的输出选择中考虑这两个向量。来自VQA: 视觉问答。
在 VQA 的早期阶段,从头开始训练视觉和语言组件,将输出传递到密集网络,并选择 n 个可能输出中的一个作为响应是合适的。
随着视觉和语言模型变得更强大,视觉问答让位于视觉语言建模(VLM),它通常被认为是对视觉问答的扩展。现代视觉语言模型不仅可以回答“这张图片中有车吗”这样简单的问题,还可以询问图片中是什么类型的车,然后询问这辆车的驾驶情况、这辆车出演过的最受欢迎的电影等。
这是视觉语言建模实际应用的一个例子。这个特定的例子来自BLIP-2 论文,我们将用它作为本帖的参考。
从 VQA 到 VLM 的转变在很大程度上是由于将大型语言模型融入视觉系统,提供了复杂的推理能力和开箱即用的百科知识。
视觉语言建模的难点是,一直以来都是多模态。你需要擅长图像、自然语言,并且需要让它们良好地协同工作。随着视觉和语言模型的不断扩大,用于将它们结合起来进行视觉语言建模的系统变得越来越复杂。
这带来了实际问题。大型语言模型是巨大的,因此更新它们的参数以学习一些新任务是昂贵的(比如,几千到几百万美元的费用)。此外,当在完全新模式的数据上训练模型时,该模型通常会灾难性遗忘;这是一个术语,用于描述模型在调整到新用例时遗忘关键信息的情况。如果你随意将图像编码器和大型语言模型结合起来,你可能会得到一个对图像和文本理解都很差的模型。
BLIP-2 论文提出了 Q-Former 来解决灾难性遗忘问题,并且通过利用现有模型来降低成本。
Q-Former 的简要介绍
如果你想在一个周末从零开始构建一个 VQA 系统,你可以考虑以下方法:
-
通过标题生成器处理你想讨论的图像
-
将用户提出的问题和生成的标题结合起来,使用一些模板创建 LLM 的提示。
-
将该提示传递给 LLM,LLM 将返回最终输出。
一个天真的方法成功的流程图。用户提出了一个能够从生成的标题中回答的问题。
如果你问的是关于图像主题的简单问题,这种方法可能会奏效,但如果你有更晦涩的问题,你可能就会失望了。
一个天真的方法未成功的流程图。用户提出了一个无法从生成的标题中回答的问题。
Q-Former 作为查询 transformer(因此得名)用于根据图像转换用户的查询。其目的是根据用户的提示从图像中提取正确的信息,并将其提供给 LLM。
Q-Former 所做的概念图。它利用提示和图像来构建 LLM 的输入。实际上,Q-Former 并不生成文本,而是生成高维嵌入,但这就是概念的本质。
BLIP-2 架构
在我们真正深入之前,先对其进行高层次的了解。
Q-Former 的稍微准确一点的描述,以及周围的组件。图像编码器将图像嵌入其最重要的部分,文本编码器对用户的提示做同样的处理,而 Q-Former 将它们结合起来,为 LLM 创建输入。
BLIP-2 架构中存在 Q-Former 的组件如下:
-
图像编码器: 一个预训练模型,将图像嵌入到抽象表示中,从而使图像分类等任务变得更容易。实际上,你可以将其看作是提取图像的重要内容。一个流行的例子是 CLIP。
-
文本编码器: 一个预训练模型,将文本嵌入到抽象表示中。这些模型通常将单词视为高维空间中的点,相似的单词会位于该空间中的相似点。一个流行的例子是 Word2Vec。
-
LLM: 一个经过训练的大型语言模型,用于执行通用语言任务。类似于聊天 GPT。
-
Q-Former: 一个将嵌入的图像和嵌入的提示结合成与 LLM 兼容的格式的 transformer 模型。Q-Former 的主要工作是正确地上下文化这两种输入,并以有利于文本生成的方式将其提供给 LLM。
由于 Q-Former 的灵活性,可以在 BLIP-2 中使用不同的编码器和 LLM。我在这篇文章中不会深入探讨这些内容,但我将很快撰写一篇关于 CLIP 图像编码的文章,并且我有一篇关于 LLM 和文本嵌入的文章,可能对不熟悉的人有所帮助:
探索现代机器学习的浪潮:逐步拆解 transformer
Q-Former,简而言之
Q-Former 的高层次概念图
首先,需要对注意力有一个整体理解,因为它构成了 Q-Former 架构的主要部分。我在这篇文章中直观且详尽地讲解了注意力机制,但基本上,注意力机制会生成修改过的输入副本,然后将这些副本混合在一起。
如果我们将文本输入“What Color is the Background”通过自注意力机制处理,那么句子中每个词的向量将与其他每个词的向量结合。这将产生一个抽象矩阵,其中包含输入中所有词的上下文信息。
多头自注意力,简而言之。这一机制在数学上将不同输入(在这个例子中是词语)的向量结合起来,生成一个矩阵,该矩阵编码了整个输入的更深层次的含义。在这篇文章中直观且详尽地解释了这一点。
即使你对注意力机制有所了解,可能仍不清楚为什么自注意力块会被分成两半。实际上,Q-Former 中的两个自注意力块实际上是一个。自注意力机制左侧的输入可以与自注意力机制右侧的输入完全互动,反之亦然。这个划分不是基于模型的工作原理,而是基于模型的训练方式。我们将在下一节中详细讨论,但要点是:由于 Q-Former 的训练方式,自注意力块擅长处理仅图像、仅文本以及同时处理这两者。因此,它有点像两个注意力块,但实际上是一个大的注意力块。
一个概念图展示了 Q-Former 中的自注意力机制如何既隔离文本和图像表示,又促进它们的交互。这是在训练的引导阶段完成的,我们将在下一节中讨论。
图表左下角学习到的标记本质上是模型在第一个自注意力块中使用的学习常量。我们稍后会详细讨论它们,但简而言之,我喜欢从两种方式来理解它们:
-
如果你从文本的自注意力角度考虑它们,它们在决定文本如何最初介绍给图像。
-
如果你从与图像互动的角度考虑它们,它们作为初始化,最终会通过图像进行修改,最终成为模型的提示。
此外,如本节的第一张图片所示,有虚线递归连接将两个前馈网络的输出连接回输入。整个区域被描绘为黄色的是一个 Q-Former 块。这些块堆叠在一起以创建完整的 Q-Former。
这就是所有组件,这可能会让人惊讶。仅仅从组件来看,并不明显为什么 Q-Former 在桥接图像和文本方面表现得特别好。要理解这一点,你需要了解 Q-Former 是如何训练的。
Q-Former 的训练方式
Q-Former 的训练可以分为两个阶段:引导和生成学习预训练。引导阶段可以进一步分为三个子阶段。我们将逐步探讨所有这些。
这些训练阶段的命名可能会有些困惑。什么是“引导”?为什么会有预训练步骤却没有“训练步骤”?我认为这些阶段的命名是以下定义的结果:
-
引导是指使用可能不完全适合最终用例的数据,以将模型从随机初始化状态提升到在相关任务中表现良好的状态的过程。
-
预训练是指使用大量数据将模型调整到一个普遍良好的状态,为最终任务做好准备。
-
微调是指将预训练模型进行调整,并提供少量任务特定数据,以优化其最终建模任务。
BLIP-2 的核心优势之一是零样本性能。BLIP-2 承诺能够在没有针对 VQA 数据集进行微调的情况下,出色地完成诸如视觉问答等任务。它使用带有图片说明的图像数据集(这些说明解释了图像的内容)来进行引导和预训练,但从未实际对 VQA 进行微调。
引导
引导阶段旨在鼓励模型在需要理解文本和图像的各种任务中表现良好。就像我的自监督学习帖子一样,你可以将其视为一种“游戏”,模型学习为视觉问答的最终任务做准备。
引导阶段有三个子阶段。这些将在后续部分中探讨,但总的来说:
-
图像-文本对比学习:模型学习如何将属于一组的图像-字幕对分在一起,并通过 对比学习 将不属于一组的图像-字幕对分开。
-
图像基础的文本生成: 将字幕分成两部分,隐藏的和未隐藏的部分,并尝试根据未隐藏的部分和图像来猜测隐藏的部分。
-
图像-文本匹配: 将 Q-Former 的输出传入 牺牲密集网络,该网络将输出转换为二元分类,然后使用此二元分类来决定一个字幕是否属于某个图像。
图像-文本对比学习
图像-文本对比学习的实际操作。图像侧的所有向量(即 LLM 的输入)都与文本侧的类标记进行比较。在这个例子中,相似度很高,因为图像和文本匹配。我们希望,如果图像和文本不匹配,最大相似度分数会很低。
在这种自举模式中,Q-Former 中的自注意力机制被分为两部分。这是通过应用于注意力层的掩码完成的,称为“单模态自注意力掩码”。这是一个复杂的短语,但表示一个简单的概念:
在自注意力机制中,每次文本侧与图像侧互动时,只需将值设置为零。
这实际上阻止了 Q-Former 图像侧和文本侧之间的所有通信。
这种训练方法还使用了一个特殊的标记,称为“类”标记。这个想法受到 BERT 的启发,这是一个我将来会详细介绍的里程碑。基本上,你有一个任意的标记,让模型知道“嘿,我们现在在进行图像-文本对比学习”。然后,你会忽略文本侧除了类标记之外的任何其他输出,并使用它来计算损失。因此,模型知道当“类”标记出现时,它是特殊的,并会尝试学习如何操控左侧的视觉和右侧的文本,以最大化对比损失的性能。
对比损失,本质上,是将匹配对拉近而将不匹配对拉远的任务。 我有一篇更详细地介绍对比损失的文章,但本质上,对比损失会查看一组图像及其标题,并尝试让模型学习哪些图像属于哪些标题。在我们的案例中,这通过计算两侧向量的相似度并找到最大相似度值来实现。匹配的文本和图像对应该具有较大的相似度分数,而不匹配的对应该具有较小的相似度分数。
通过执行这种自举操作,我们鼓励模型学习对齐图像和文本。换句话说,我们正在训练模型来学习哪些图像与某段文本相关,哪些则不相关。
单模态自注意力的概念图,这是在此训练阶段使用的掩码策略。请注意图像端和文本端都有完全的注意力,但两者之间没有注意力。
图像基础文本生成
“图像基础文本生成”预训练步骤的概念图。在这里,Q-Former 的一个输入文本部分被隐藏,Q-Former 的任务是尝试填补隐藏的文本。
在这种自举模式下,我们要求 Q-Former 完成一个部分隐藏的标题。我们应用了“多模态因果自注意力掩码”,允许 Q-Former 的文本端与图像端交互,但隐藏了部分需要由 Q-Former 预测的标题。我们还将“类别”标记替换为“解码器”标记,以让模型知道它应该执行什么任务。
多模态因果自注意力的概念图;这是在此训练阶段使用的掩码策略。请注意,除了那些模型应输出的标记外,所有标记之间都允许完全的注意力。
图像-文本匹配
“图像-文本匹配”预训练步骤的概念图。在这个阶段没有使用掩码,允许所有文本和图像标记在自注意力机制中进行交互。请注意,输出是错误的,因为图像与文本“A Painting of a Monster Truck”不兼容。
在这种预训练模式下,我们创建了一个临时线性分类器(一个密集网络),并将 Q-Former 的所有输出标记输入其中。这个线性分类器将标记投影为“真实”或“虚假”预测,用于训练模型预测输入文本是否与输入图像匹配。不同的组合对,无论是匹配还是不匹配的组合,都被输入到模型中。
我在这篇文章中讨论了使用密集网络来投影模型输出用于某些预训练任务的概念。本质上,线性分类器用于训练,但在推理时被丢弃,这有助于模型学习文本和图像的一般表示,但有助于防止模型在任务中过于专业化;如此专业化,以至于在将标记传递给 LLM 的实际任务中表现较差。
你可以把 Q-Former 想象成“理解文本和图像”的部分,而临时线性分类器则是“将这种理解转化为是或否的回答”的部分。在这一步之后,我们会丢弃“将这种理解转化为是或否的回答”的部分,保留一般的文本和图像理解。
从自举中得到什么
在上一节中,我们讨论了自举的三个阶段:图像-文本对比学习、图像基础文本生成和图像-文本匹配。通过优化 Q-Former 以完成这些各种任务,Q-Former 被鼓励建立图像和文本的强表示,并建立一个强大的系统来关联这两者。
关于学习到的标记的说明
如前所述,学习到的标记(在 BLIP-2 论文中称为“查询向量”)与图像和文本交互以提取关键信息。为了进一步阐述这一点,我想分享 BLIP-2 论文中关于查询向量的以下引文:
关于查询向量的一般情况:
查询通过自注意力层相互作用,并通过交叉注意力层(插入每隔一个变换器块)与冻结的图像特征互动。查询还可以通过相同的自注意力层与文本交互。
关于自举阶段如何与查询向量相关:
我们的目标是训练 Q-Former,使查询能够学习提取对文本最具信息性的视觉表示。
关于查询向量如何关联文本和图像信息:
由于 Q-Former 的架构不允许冻结的图像编码器和文本标记之间直接交互,因此生成文本所需的信息必须首先由查询提取,然后通过自注意力层传递给文本标记。因此,查询被迫提取能够捕捉文本所有信息的视觉特征。
预训练
现在我们有了一个在文本和图像上具有良好内部表示的 Q-Former,我们可以将其连接到 LLM 上,并使用它来训练 Q-Former。
BLIP-2 的生成预训练图
我们可以将图像的描述分成两部分,一个前缀和一个后缀。我们可以通过整个 BLIP-2 架构传递前缀,并修改 Q-Former 的权重以鼓励 LLM 的输出为后缀。从概念上讲,这使图像和文本的表示在 Q-Former 中与特定 LLM 模型的需求对齐。
理论总结
太好了,现在我们了解了 BLIP-2 架构;组件、Q-Former(其核心组件)如何工作以及如何进行训练。在下一节中,我们将使用预训练的 BLIP-2 进行图像描述、VQA,甚至进行小型图像基础对话。
整个BLIP-2架构
在BLIP-2 论文中定义的 BLIP-2 架构与 Q-Former 的关系。这里我们可以看到 Q-Former 的输入、学习到的查询、三种训练引导策略和支持这些策略的三种掩码。一个微妙的说明:BLIP-2 每隔一个块就将图像暴露给 Q-Former。我邀请你自行思考这样做的理由。对我来说,这感觉像是一种残差连接,鼓励对图像进行反复且越来越复杂的分析。没有图像访问权限的块可以形成复杂的关系,然后在随后的块中与图像进行比较。
使用 Hugging Face 的 Q-Former 进行 VQA
在未来的帖子中,我会从零开始编写和训练一个 Q-Former,但我认为你会同意这篇帖子已经足够长了。现在让我们使用预构建的解决方案来实验 Q-Formers。
完整的笔记本可以在这里查看:
[## MLWritingAndResearch/VQAWithQFormer.ipynb at main · DanielWarfield1/MLWritingAndResearch
用于机器学习写作和研究的笔记本示例 - MLWritingAndResearch/VQAWithQFormer.ipynb at main ·…
github.com](https://github.com/DanielWarfield1/MLWritingAndResearch/blob/main/VQAWithQFormer.ipynb?source=post_page-----353d42791054--------------------------------)
由 SalesForce 内的机器学习团队 LAVIS(发布 BLIP-2 论文的团队)慷慨地提供了 Hugging Face 上的端到端预训练解决方案:
"""Downloading the BLIP-2 Architecture
loading as an 8 bit integer to save on GPU memory. This may have some impact on
performance.
"""
from transformers import AutoProcessor, Blip2ForConditionalGeneration
import torch
processor = AutoProcessor.from_pretrained("Salesforce/blip2-opt-2.7b")
model = Blip2ForConditionalGeneration.from_pretrained("Salesforce/blip2-opt-2.7b", device_map="auto", load_in_8bit=True) # load in int8
如你所见,BLIP-2 包含两个部分;一个处理器和一个模型。首先,让我们探讨处理器。
处理器
在 HuggingFace 提供的示例中,处理器用于在将输入(包括文本和图像)传递给 BLIP-2 之前进行预处理。让我们加载一张图像,生成一些文本,并将其传递给处理器,看看结果如何。
"""Loading and displaying a sample image
"""
import requests
from PIL import Image
url = 'https://github.com/DanielWarfield1/MLWritingAndResearch/blob/main/Assets/Images/pexels-thuany-marcante-1805053.jpg?raw=true'
image = Image.open(requests.get(url, stream=True).raw).convert('RGB')
print(f'Image dimensions: {image.width}px X {image.height}px')
dsfact = 15
display(image.resize((int(image.width/dsfact), int(image.height/dsfact))))
我们将使用的一个示例图像
将这张图片和一些示例文本传递给处理器后,我们可以看到从处理器获得了一个字典:
"""Exploring the outputs of the processor
"""
processor_result = processor(image, text='a prompt from the user about the image', return_tensors="pt").to("cpu", torch.float16)
processor_result.keys()
从处理器获得的 pixel_values 是将图像缩小到 224 x 224 的变换,颜色值被标准化到适合建模的范围。
"""Understanding resolution and plotting one of the color channels
"""
import matplotlib.pyplot as plt
#printing the processed image shape
print(f'processed image shape: {processor_result["pixel_values"].numpy().shape}')
#extracting one of the color channels from the processed image
print('single color channel:')
processed_im_c0 = processor_result['pixel_values'].numpy()[0,0]
#rendering
plt.imshow(processed_im_c0, interpolation='nearest')
plt.show()
"""Understanding the distribution of values allong each color channel,
both in the processed image and in the original image
"""
bins = 100
#extracting all color channels from the processed image
processed_im_c1 = processor_result['pixel_values'].numpy()[0,1]
processed_im_c2 = processor_result['pixel_values'].numpy()[0,2]
#plotting modified pixel value distributions
plt.figure()
plt.hist([processed_im_c0.flatten(),
processed_im_c1.flatten(),
processed_im_c2.flatten()], bins, stacked=True, density = True)
plt.title('processed image value distribution')
plt.show()
#plotting original pixel value distributions
import numpy as np
image_np = np.array(image)
plt.figure()
plt.hist([image_np[:,:,0].flatten(),
image_np[:,:,1].flatten(),
image_np[:,:,2].flatten()], bins, stacked=True, density = True)
plt.title('raw image value distribution')
plt.show()
处理过的图像的值被缩减到一个合理的范围,并且它们似乎平均接近于零
原始图像的值具有偏倚分布,覆盖了更宽的值范围
从处理器获得的 input_ids 是词片索引。句子的各个部分被分配了单独的索引,这些索引后来被用于词向量嵌入器,然后应用于 BLIP-2。
"""Exploring the input_ids from the processor given a variety of prompts
"""
print('input_ids for "a short prompt":')
sampres = processor(image, text='a short prompt', return_tensors="pt").to("cpu", torch.float16)
print(sampres['input_ids'])
print('input_ids for "a much much much much longer prompt":')
sampres = processor(image, text='a much much much much longer prompt', return_tensors="pt").to("cpu", torch.float16)
print(sampres['input_ids'])
print('input_ids for "alongcompoundword":')
sampres = processor(image, text='alongcompoundword', return_tensors="pt").to("cpu", torch.float16)
print(sampres['input_ids'])
由于我们是在推断模型,因此处理器提供的掩码只是全 1,允许模型看到所有输入值。
"""Understanding the mask from the processor
"""
print('input_ids for "a short prompt":')
sampres = processor(image, text='a short prompt', return_tensors="pt").to("cpu", torch.float16)
print(sampres['input_ids'])
print('mask for "a short prompt":')
print(sampres['attention_mask'])
调用模型
现在我们对处理器的功能有了初步了解,我们可以开始使用它将数据传递给 BLIP-2,并开始生成输出。
图像标题生成: 如果你提供图像而没有文本,BLIP-2 将为图像生成标题。
"""Getting BLIP-2 to describe the image, unprompted
this is done by only passing the image, not the text
"""
inputs = processor(image, return_tensors="pt").to(device, torch.float16)
generated_ids = model.generate(**inputs, max_new_tokens=20)
generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip()
print(generated_text)
提示图像标题生成: 如果你为标题提供一个前缀,BLIP-2 将尝试完成这个标题。
"""Prompted caption example 1
"""
prompt = "this is a picture of"
inputs = processor(image, text=prompt, return_tensors="pt").to(device, torch.float16)
generated_ids = model.generate(**inputs, max_new_tokens=20)
generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip()
print(generated_text)
完成提示“这是一张……的图片”的结果
"""Prompted caption example 2
"""
prompt = "the weather looks"
inputs = processor(image, text=prompt, return_tensors="pt").to(device, torch.float16)
generated_ids = model.generate(**inputs, max_new_tokens=20)
generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip()
print(generated_text)
完成提示“天气看起来……”的结果
视觉问答: 通过调用格式化特殊查询的 BLIP-2,可以实现视觉问答,而无需经过视觉问答数据的训练。
prompt = "Question: what season is it? Answer:"
inputs = processor(image, text=prompt, return_tensors="pt").to(device, torch.float16)
generated_ids = model.generate(**inputs, max_new_tokens=20)
generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip()
print(generated_text)
基于视觉的对话: 我们可以将提示格式化成类似对话的内容,从而与模型就图像进行对话。
"""Visual Conversation
conversing about the image
"""
#imagine these are generated by a person as a response to output, rather than pre-defined.
questions = [
"What's in this photo?",
"What is vernacular architecture?"
]
#defining the state of the conversation as it progresses, to be passed to the model
conv_state = ''
#asking all questions in order
for question in questions:
#updating the conversational state with the question
conv_state = conv_state+' Question: ' + question + ' Answer: '
#passing the state thus far to the model
inputs = processor(image, text=conv_state, return_tensors="pt").to(device, torch.float16)
#generating a response
generated_ids = model.generate(**inputs, max_new_tokens=40)
generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip()
#rendering conversation
print('Question: '+question)
print('Answer: ' + generated_text)
#updating the conversational state with the answer
conv_state = conv_state + generated_text + '\n'
结论
在这篇文章中,我们回顾了多模态图像和语言建模的发展历史;从最初的视觉问答,到现代阶段使用大型语言模型和图像编码器。我们描述了一种将图像暴露给大型语言模型的方法,即 BLIP-2 架构,并描述了其最重要组件 Q-Former 的内部工作原理。接着,我们探索了 BLIP-2 在标题生成、视觉问答和基于视觉的对话中的实际应用。
关注以获取更多信息!
我描述机器学习领域的论文和概念,重点是提供实用和直观的解释。我计划在未来的文章中从零开始实现一个 Q-Former。
归属声明: 本文档中的所有图片均由丹尼尔·沃菲尔德创作,除非另有来源说明。您可以将此帖中的任何图片用于非商业目的,只要您注明此文章,danielwarfield.dev
,或两者皆可。
使用 Python 可视化卫星图像的 RGB 通道
原文:
towardsdatascience.com/visualising-the-rgb-channels-of-satellite-images-with-python-6d541af1f98d
在可视化卫星图像时,如何处理多个光谱波段、大像素值和倾斜的 RGB 通道
·发表于 Towards Data Science ·阅读时长 6 分钟·2023 年 4 月 11 日
–
(来源:SWED)
卫星图像包含大量信息。缺点是它们的可视化并不简单。与普通图像不同,卫星图像可能具有:
-
超过 12 个通道
-
大像素值
-
倾斜的像素值
我们将讨论这些关键考虑因素。然后,我们将这些因素考虑到一个 Python 函数中,这样你在组合 RGB 通道时就能拥有更大的灵活性。具体来说,它允许你调整图像的亮度和色调。代码已给出,完整项目可以在 GitHub 上找到。
导入和数据集
我们的第一个导入是 地理空间数据抽象库(gdal)。这在处理遥感数据时非常有用。我们还引入了更多标准的 Python 包(第 4-5 行)。最后,glob 用于处理文件路径(第 7 行)。
# Imports
from osgeo import gdal
import numpy as np
import matplotlib.pyplot as plt
import glob
我们将可视化来自测试集的图像 Sentinel-2 水边数据集 **(**SWED)。该数据集包含来自 49 个位置的 98 张海岸线图像。我们将在下方加载这些图像的所有路径。
#Load paths
paths = glob.glob("../data/SWED/test/images/*")
关键考虑因素
光谱波段数量
我们加载第一张图像(第 2 行),并输出其形状(第 3 行)。这给出的值为**(12,256,256)**。换句话说,每张图像由 12 个通道的 256x256 像素组成。如果这是一个普通图像,我们会期望其维度为(256,256,3)。也就是说,每个像素有 3 个通道——RGB。
#Load first image
img = gdal.Open(paths[0]).ReadAsArray()
img.shape #(12,256,256)
12 个通道是哨兵-2 光谱带。每个光谱带测量的是从地球表面反射的不同波长的电磁辐射。这包括人眼无法看到的红外光。在可视化图像时,我们只对可见光光谱带感兴趣。
对于我们的特定图像,显光带位于位置 3(红色)、2(绿色)和 1(蓝色)。我们在下面选择这些。我们还转置了数组,使其维度为(256,256,3)。
#Get RGB image
rgb = img[[3,2,1]].transpose(1,2,0)
大像素值
为了捕捉尽可能多的细节,卫星图像具有大的动态范围。因此,像素值不会落入典型的[0, 255]范围内。对于我们的图像,打印最小和最大像素值给出的是150 和 8600。
# Pixel range
print(rgb.min(),rgb.max()) #150 8,600
哨兵-2 图像具有最大反射值为 10000。虽然像素值有时会高于此值,但在可视化 RGB 通道时,我们可以忽略这些大值。因此,我们通过将图像除以 10000 并剪裁到 0 和 1 之间来缩放图像。这确保所有像素值都在 0 和 1 之间。
#Scale image
rgb = np.clip(rgb/10000,0,1)
偏向的像素值
现在我们可以使用 matplotlib 显示我们的卫星图像(第 2 行)。你会注意到图像的亮度/对比度较低。这在显示 RGB 通道时是典型的。
#Display RGB image
plt.imshow(rgb)
图 1:低亮度的 RGB 通道(来源:作者)(数据集:SWED)
如前所述,卫星图像的动态范围很大。这是为了捕捉更广泛的像素范围——从非常亮到非常暗。结果是,像素往往偏向较低的值。你可以在下面的直方图中看到这一点。这些直方图显示了上述图像的 RGB 通道的像素频率。
图 2:卫星图像 RGB 通道的偏向像素值(来源:作者)
#Display histograms of pixel intesity for each band
fig, axs = plt.subplots(1,3,figsize=(18,5))
fig.patch.set_facecolor('xkcd:white')
labels = ['Red','Green','Blue']
for i,ax in enumerate(axs):
ax.hist(rgb[:,:,i].flatten(),bins=100)
ax.set_title(labels[i],size=20,fontweight="bold")
ax.set_xlabel("Pixel Value",size=15)
ax.set_ylabel("Frequency",size =15)
获取更好图像的一个简单方法是剪裁每个通道的像素值范围(第 2 行)。我们只取像素值从 0 到 0.3,并将它们缩放回 0 到 1 之间。在图 3中,你可以看到结果图像更亮。
# Clip RGB image to 0.3
rgb = np.clip(rgb,0,0.3)/0.3
plt.imshow(rgb)
图 3:高亮度的 RGB 通道(来源:作者)(数据集:SWED)
整合所有内容
对于快速可视化,上述内容足矣。如果你想对过程有更多控制,可以使用下面的函数。这将缩放图像,选择 RGB 通道,并使用不同的截止值剪裁每个通道。还有一个显示选项,将输出带有所选截止值的 RGB 直方图。
def visualise_rgb(img,clip=[0.3,0.3,0.3],display=True):
"""Visulaise RGB image with given clip values and return image"""
# Scale image
img = np.clip(img/10000,0,1)
# Get RGB channels
rgb = img[[3,2,1]]
#clip rgb values
rgb[0] = np.clip(rgb[0],0,clip[0])/clip[0]
rgb[1] = np.clip(rgb[1],0,clip[1])/clip[1]
rgb[2] = np.clip(rgb[2],0,clip[2])/clip[2]
rgb = rgb.transpose(1,2,0)
if display:
#Display histograms of pixel intesity with given clip values
fig, axs = plt.subplots(1,4,figsize=(22,5))
fig.patch.set_facecolor('xkcd:white')
labels = ['Red','Green','Blue']
for i,ax in enumerate(axs[0:3]):
ax.hist(img[3-i].flatten(),bins=100)
ax.set_title(labels[i],size=20,fontweight="bold")
ax.axvline(clip[i],color="red",linestyle="--")
ax.set_yticks([])
#Display RGB image
axs[3].imshow(rgb)
axs[3].set_title("RGB",size=20,fontweight="bold")
axs[3].set_xticks([])
axs[3].set_yticks([])
return rgb
你可以在下面看到这个功能的实际效果。在这个案例中,我们使用 0.3 的截止值剪切每个通道。图 4 显示了这些截止值的直方图和结果 RGB 图像。这在尝试不同截止值时可能会很有用。
img = gdal.Open(paths[0]).ReadAsArray()
rgb = visualise_rgb(img,[0.3,0.3,0.3])
图 4:RGB 通道可视化功能的输出(来源:作者)(数据集:SWED)
调整亮度
不同的图像会有不同的最佳截止值。实际上,本文的封面图像是使用 3 个不同的值创建的。上述功能使调整这些值变得简单。在图 5中,你可以看到调整截止值如何改变亮度。
rgb_1 = visualise_rgb(img,[0.15,0.15,0.15],display=False)
rgb_2 = visualise_rgb(img,[0.3,0.3,0.3],display=False)
rgb_3 = visualise_rgb(img,[0.45,0.45,0.45],display=False)
图 5:调整卫星图像的亮度(来源:作者)(数据集:SWED)
调整颜色(色调)
它还可以让你对每个颜色通道有更多控制。如图 6所示,如果我们降低某个通道的截止值,该颜色将变得更加突出。这可以帮助你微调可视化效果。
rgb_1 = visualise_rgb(img,[0.2,0.3,0.3],display=False)
rgb_2 = visualise_rgb(img,[0.3,0.3,0.2],display=False)
rgb_3 = visualise_rgb(img,[0.3,0.2,0.3],display=False)
图 6:调整卫星图像的颜色(来源:作者)(数据集:SWED)
在处理遥感数据时,通常重要的是可视化你的图像。这将帮助你建立对问题的直觉。也许更重要的是,你可以创建用于向他人解释工作的美丽图像。上述功能将帮助你创建最引人注目的图像。请记住,你可能需要根据你的卫星图像数据集进行调整。
希望你喜欢这篇文章!你可以通过成为我的 推荐会员 来支持我 😃
[## 通过我的推荐链接加入 Medium — Conor O’Sullivan
作为 Medium 会员,你的会员费的一部分将会分配给你阅读的作者,你将可以访问所有故事…
conorosullyds.medium.com](https://conorosullyds.medium.com/membership?source=post_page-----6d541af1f98d--------------------------------)
| Twitter | YouTube | Newsletter — 免费注册获取 Python SHAP 课程
数据集
Sentinel-2 水边数据集(SWED)(许可:Sentinel 数据法律声明) openmldata.ukho.gov.uk/
参考文献
GISGeography,Sentinel 2 波段及组合(2022),gisgeography.com/sentinel-2-bands-combinations/
Csaba,在 Python 中创建 Sentinel-2 RGB 合成图像(2021),www.satmapper.hu/en/rgb-images/
使用 Plotly Express 在 3D 线图上可视化井路径
原文:
towardsdatascience.com/visualising-well-paths-on-3d-line-plots-with-plotly-express-de73ca73dea3
使用 Plotly Express 的 3D 线图
·发布于 Towards Data Science ·阅读时间 7 分钟·2023 年 6 月 4 日
–
使用 Plotly Express 进行的 3D 井路径可视化。图片由作者提供。
可视化是我们理解井记录数据和地下数据的关键任务之一。这包括在井记录图、散点图和直方图上查看数据。通过这样做,我们可以对数据有一个可靠的理解。然而,处理 2D 图有时是不够的,我们需要通过 3D 增加额外的维度。
在岩石物理学和地球科学中,3D 可视化的一个优秀应用案例是可视化井的路径。
在石油和天然气勘探的早期阶段,井是垂直钻入地下的。然而,随着技术的进步,行业从垂直钻探转向水平钻探,最终使用地质导向的创新技术钻探复杂的井路径。
井路径几何的这种演变突显了在三维中可视化井路径的重要性。这样做可以让我们更好地理解井如何穿透地质层,并规划未来的井以避免碰撞或问题。
在本文中,我们将探讨如何利用记录井位置的井调查数据,并使用 Plotly Express 的 3D 线图展示这些数据。
数据
在这个简短的教程中,我们将使用来自荷兰北海区域八口井的勘探数据。这些数据来源于提供免费使用的井记录数据的NLOG.nl网站,这些数据是过去几十年中获取的。
这些文件包含以下列:
-
MD:测量深度(米)
-
INC:钻孔倾斜度(度)
-
AZI:钻孔方位角(度)
-
TVD:真实垂直深度(米)
-
XOFFSET:井在 X 方向上的位置(米)
-
YOFFSET:井在 Y 方向的位置(米)
为了简化数据加载,每个文件中的列名都是相同的。
数据集的完整细节可以在文章末尾找到。
此外,本教程中使用的所有井数据都具有相同的来源。我将在未来的文章中讲解如何将 XOFFSET 和 YOFFSET 转换为网格坐标。
设置数据
首先,我们需要导入多个库。对于这篇文章,我们将相对简单地导入pandas — 用于读取我们的csv
数据,和Plotly Express — 用于创建我们的可视化,还有os — 用于读取包含文件的目录。
import pandas as pd
import plotly.express as px
import os
我们可以通过几种方式将数据加载到 Python 和Pandas中。
为了简化操作,我保留了长形式的代码,以帮助那些新手理解发生了什么。
首先,我们需要创建一个空列表来存储我们的文件路径。
# Create empty list to store file paths
survey_data = []
接下来,我们将创建一个变量来存储包含调查数据的 CSV 文件的文件路径位置。
# Set up the file path location. This can be a relative or absolute path
file_path = 'Data/Notebook 43/'
最后,我们可以遍历file_path
目录中的每个文件,并检查是否有扩展名为.csv 的文件。一旦找到这些文件,完整的文件路径将被附加到 file_path 列表中。
# Loop through each file within the file_path location
for f in os.listdir(file_path):
# Check for CSV files
if f.endswith('.csv'):
# Add CSV file location to the survey_data list
survey_data.append(f'{file_path}{f}')
survey_data
当我们查看survey_data
列表时,我们得到以下内容:
['Data/Notebook 43/NLOG - A12-A-01 - Survey.csv',
'Data/Notebook 43/NLOG - A12-A-06 - Survey.csv',
'Data/Notebook 43/NLOG - A12-A-07 - Survey.csv',
'Data/Notebook 43/NLOG - A12-A-02 - Survey.csv',
'Data/Notebook 43/NLOG - A12-A-05 - Survey.csv',
'Data/Notebook 43/NLOG - A12-A-07-ST1 - Survey.csv',
'Data/Notebook 43/NLOG - A12-A-04 - Survey.csv',
'Data/Notebook 43/NLOG - A12-A-03 - Survey.csv']
一旦我们有了文件路径,我们需要使用 pandas 中的.concat
和map
函数将它们合并成一个pandas数据框。
df = pd.concat(map(pd.read_csv, survey_data))
当我们查看数据框(df
)时,我们将得到以下内容。
这是来自八个荷兰部门石油和天然气井的合并井调查数据的数据框。图片由作者提供。
或者,如果你是一个更高级的 Python 用户,并且希望寻找更高效的方法将多个文件直接加载到pandas数据框中,你可以使用以下函数。
如你所见,它更加紧凑。
# Alternatively:
import glob
df = pd.concat(map(pd.read_csv, glob.glob('Data/Notebook 43/*.csv')))
为了确认我们已经加载了所有的井数据,我们可以调用WELL列并查看其中的唯一值。
df['WELL'].unique()
这将返回一个包含 8 个井的数组。
array(['A12-A-01', 'A12-A-06', 'A12-A-07', 'A12-A-02', 'A12-A-05',
'A12-A-07-ST1', 'A12-A-04', 'A12-A-03'], dtype=object)
创建一个交互式的 Plotly Express 3D 折线图
对于这个例子,我们将使用Plotly Express 的 3D 折线图而不是使用 matplotlib 的 3D 图。从我的经验来看,这种方式生成和处理 3D 图似乎更快、更高效、更简便。
要创建我们的3D 折线图(Line Plot),我们首先需要创建一个图形并将其分配给px.line_3d()
。
在此函数中,我们将传入我们的 XOFFSET、YOFFSET、TVD 和 WELL 列。
这将允许我们绘制每个井的 X、Y 和 TVD(真实垂直深度)位置,并通过 WELL 列用颜色区分它们。
fig = px.line_3d(x=df.XOFFSET,
y=df.YOFFSET,
z=df.TVD,
color=df.WELL)
fig.show()
一旦我们运行上述代码,我们会得到如下图形。
Plotly Express 3D 线条图显示了在调整比例和线条之前的井路径情况。图片由作者提供。
从图中可以明显看出,我们需要调整它,以获得更好的效果。
这个图表存在一些问题,我们需要解决:
-
整体图形较小
-
线条宽度非常细
-
井路径颠倒了
我们可以通过一些调整来解决这个问题。
首先,我们将更新图表的布局,并定义一个width
和height
参数。
然后,我们将更新包含 TVD 测量的 z 轴,并将其设置为反向比例。
最后,我们可以通过调用update_traces()
来加粗线条。
fig = px.line_3d(x=df.XOFFSET,
y=df.YOFFSET,
z=df.TVD,
color=df.WELL)
fig.update_layout(width=800,
height=800,
autosize=False,
scene={'zaxis':{'autorange':'reversed'}})
fig.update_traces(line={'width':10})
fig.show()
当我们运行上述代码时,我们会得到如下互动图形。
Plotly Express 3D 线条图显示了在调整比例和线条后井路径的情况。图片由作者提供。
我们可以看到,井路径现在正确定位,并且我们有了一个稍微大的图形进行操作。
以这种方式可视化数据使我们能够看到井的去向,以及是否有任何井的侧钻。如果我们专注于规划未来的井,我们能够在过程早期识别任何潜在问题。
总结
清晰地可视化井路径是理解井位置的绝佳方法。它不仅可以显示井在地下的位置,还能显示相对于其他附近井的位置。
在这个简短的教程中,我们已经了解了如何利用 Python 和 Plotly Express 库,在互动 3D 图上可视化多个井路径。这使我们能够以易用的方式深入了解井的具体位置。
在你的下一个项目中试试看吧。
数据详情
本教程中使用的数据下载自NLOG.nl,这是一个包含整个荷兰北海区域井记录数据的网站。数据可以免费下载和使用。数据许可证的完整细节可以在这里找到,但知识产权部分提供了使用的摘要:
NLOG.NL 不对通过本网站提供的信息(域名、商标权、专利及其他知识产权除外)主张任何权利。用户可以在不事先获得 NLOG.NL 的书面许可或合法授权方同意的情况下,复制、下载、公开、分发或简化本网站提供的信息。用户还可以复制、重复、处理或编辑这些信息和/或布局,前提是标明 NLOG.NL 作为来源。
感谢阅读。在离开之前,你一定要订阅我的内容,将我的文章直接送到你的收件箱。 你可以在这里完成订阅!或者,你也可以 注册我的新闻通讯 以便将额外的内容直接免费送到你的收件箱。
其次,你可以通过注册会员获得完整的 Medium 体验,并支持成千上万的其他作家和我。它只需每月 $5,你将全面访问所有精彩的 Medium 文章,并有机会通过写作赚取收入。
如果你通过 我的链接注册,你将直接用部分费用支持我,而且不会增加你的成本。如果你这样做了,非常感谢你的支持。
Arabica 中的可视化模块加速了文本数据的探索
原文:
towardsdatascience.com/visualization-module-in-arabica-speeds-up-text-data-exploration-47114ad646ce
Arabica 现在提供了 unigram、bigram 和 trigram 词云、热图和折线图,以进一步加速时间序列文本数据分析。
·发表于 Towards Data Science ·阅读时长 6 分钟·2023 年 1 月 9 日
–
图 1. Bigram 词云,作者提供的图像。
介绍
Arabica 是一个用于探索性文本数据分析的 Python 库,专注于从时间序列的角度分析文本。它反映了许多文本数据集作为时间上重复观察的经验现实。时间序列文本数据包括新闻文章标题、研究文章摘要和元数据、产品评论、社交网络通信等。 Arabica 通过提供这些方法简化了这些数据集的探索性分析(EDA):
-
arabica_freq: 描述性 n-gram 分析和时间序列 n-gram 分析,用于基于 n-gram 的文本数据集 EDA
-
cappuccino: 用于数据的可视化探索。
本文介绍了Cappuccino,Arabica 的时间序列文本数据探索性分析可视化模块。请阅读文档和教程这里以获取对 Arabica 的一般介绍。
EDIT Jan 2023*: Arabica 已经更新。请查看* 文档 以获取参数的完整列表。
2. Cappuccino,探索性文本数据分析的可视化
实现的图表包括词云(unigram、bigram 和 trigram 版本)、热图和折线图。它们帮助发现(1)最频繁的 n-grams,反映其时间序列特征(词云)和(2)n-grams 随时间的发展(热图、折线图)。
这些图表设计用于演示、报告和实证研究。因此,它们具有高分辨率(像素取决于图表中显示的数据范围)。
Cappuccino 依赖于matplotlib、worcloud、和plotnine来创建和展示图表,同时使用cleantext和NTLK停用词语料库进行预处理。Plotnine 将广泛使用的ggplot2库实现到了 Python 中。要求在这里。
方法的参数如下:
def cappuccino(text: str, # Text
time: str, # Time
plot: str = '', # Chart type: 'wordcloud'/'heatmap'/'line'
ngram: int = '', # N-gram size, 1 = unigram, 2 = bigram, 3 = trigram
time_freq: str= '', # Aggregation period: 'Y'/'M'', if no aggregation: 'ungroup'
max_words int = '', # Max number for most frequent n-grams displayed for each period
stopwords: [], # Languages for stop words
skip: [ ], # Remove additional strings
numbers: bool = False, # Remove numbers
punct: bool = False, # Remove punctuation
lower_case: bool = False # Lowercase text
)
3. 描述性 n-gram 可视化
Arabica 中的描述性分析提供了 n-gram 频率计算,但不对特定时期进行汇总。简单来说,首先计算每个文本记录的 n-grams 频率,其次,将这些频率对整个数据集进行汇总,最后,将这些频率可视化到图表中。
词云
让我们以百万新闻标题为例,这些新闻标题按日发布,数据集的时间范围是 2003–2–19 到 2016–09–18。该数据集由澳大利亚广播公司提供,采用CC0: 公共领域许可证。我们将数据子集化为前 50,000 条标题。
首先,使用 pip install arabica
安装 Arabica,然后导入 Cappuccino:
from arabica import cappuccino
使用 pandas
读取数据后,数据如下所示:
图 2. 百万新闻标题数据
我们将文本转换为小写,清理数据中的标点符号和数字,去除英文停用词和其他不需要的字符串(“g”、“br”),并绘制包含 100 个最频繁单词的词云:
cappuccino(text = data['headline'],
time = data['date'],
plot = 'wordcloud',
ngram = 1, # n-gram size, 1 = unigram, 2 = bigram, 3 = trigram
time_freq = 'ungroup', # no period aggregation
max_words = 100, # displays 100 most frequent words
stopwords = ['english'], # remove English stopwords
skip = ['g','br'], # remove additional strings
numbers = True, # remove numbers
punct = True, # remove punctuation
lower_case = True # lowercase text
)
它返回词云:
图 3. 词云,作者图片。
在将 ngram = 2
更改后,我们获得了包含 100 个最频繁二元组的词云(见封面图片)。另外,ngram = 3
显示最频繁的三元组:
图 4. 词云 — 三元组,作者图片。
4. 时间序列 n-gram 可视化
时间序列文本数据通常显示出随时间变化的波动。选举前的政治声明和新冠疫情期间的新闻标题就是很好的例子。为了展示时间段内的 n-grams,Arabica 实现了热图和折线图用于按月和按年展示。
作者图片,来源:Draw.io
热图
一个显示每个月十个最频繁单词的热图,代码如下:
cappuccino(text = data['headline'],
time = data['date'],
plot = 'heatmap',
ngram = 1, # n-gram size, 1 = unigram, 2 = bigram
time_freq = 'M', # monthly aggregation
max_words = 10, # displays 10 most frequent words for each period
stopwords = ['english'], # remove English stopwords
skip = ['g', 'br'], # remove additional strings
numbers = True, # remove numbers
punct = True, # remove punctuation
lower_case = True # lowercase text
)
单元词热图是输出结果:
图 5. 热图 — 单元词,作者图片。
单元组热图让我们首次了解数据的时间变异性。我们可以清晰地识别数据中的重要模式:
最常见的 n-grams:“us”、“警察”、“新”、“人”。
离群值(仅在一个周期内出现的术语):“战争”、“wa”、“雨”、“被杀”、“伊拉克”、“担忧”、“预算”、“巴厘岛”。
我们可以考虑在分析的后期去除离群值。或者,通过更改 ngram = 2
和 max_words = 5
创建一个二元组 热图,显示每个周期中最常见的五个二元组。
图 6. 热图 — 二元组,作者提供的图像。
线图
通过更改 plot = ‘line’
显示带有 n-grams 的线图。通过将 ngram
参数设置为 1 和 max_words = 5
,我们为每个周期中的五个最常见的词创建一个线图:
图 7. 线图 — 单元组,作者提供的图像。
类似地,通过更改 ngram = 2
和 max_words = 3
,二元组线图看起来像这样:
图 8. 线图 — 二元组,作者提供的图像。
最终备注
Cappuccino 在可视化探索具有时间序列特征的文本数据方面大有帮助。只需一行代码,我们就能预处理数据并提供数据集的初步探索性视图。以下是一些建议:
-
可视化的频率也取决于数据中时间维度的长度。在长时间序列中,月度图无法清晰显示数据,而在短时间序列(少于一年)的年频率图则无法提供时间上的变异性。
-
根据项目中的数据集选择合适的可视化形式。对于 n-gram 变异性高的数据集(见图 8),线图不是一个好的选择。在这种情况下,即使每个周期中有许多 n-grams,热图也能更好地显示数据。
我们可以用 Arabica 回答的一些问题是 (1) 特定领域(经济学、生物学等)的概念如何随时间演变,使用研究文章元数据,(2) 在总统竞选期间,哪些关键主题被强调,使用Twitter 推文,(3) 公司应该改进品牌和沟通的哪些部分,使用客户产品评论。
本教程中的完整代码在我的 GitHub。欲了解更多示例,请阅读 文档 和关于 arabica_freq 方法的 教程。
编辑: Arabica 现在有了一个 情感和结构性断裂 分析模块。阅读更多内容并查看这些教程中的实际应用:
-
时间序列文本数据中的情感分析和结构性突破
-
N-gram 和情感分析的客户满意度测量
你喜欢这篇文章吗?你可以邀请我 喝咖啡 并支持我的写作。你也可以订阅我的 电子邮件列表 以获取关于我新文章的通知。谢谢!
照片由 Kanwardeep Kaur 拍摄,来源于 Unsplash
嵌入的可视化
可视化高维数据的方式不止一种。在这里,我们回顾了人工智能的发展历史,以探索这些可视化的演变。
·
关注 发表在 Towards Data Science ·7 分钟阅读·2023 年 5 月 27 日
–
我在 1990 年将我的第一篇关于人工智能的论文提交到一个小型地方会议——“中西部人工智能与认知科学学会”。在那些日子里,人工智能领域完全被“符号”研究所定义。这种方法被称为“古老的人工智能”或 GOFAI(发音为“go fi”,类似“wifi”)。我们这些现在被称为“深度学习”的研究者不得不真的争论我们所研究的内容是否应被视为人工智能。
被排除在人工智能之外是把双刃剑。一方面,我不同意当时定义的人工智能的基本信条。基本假设是“符号”和“符号处理”必须是所有人工智能的基础。因此,我很高兴能在一个甚至不被认为是人工智能的领域工作。另一方面,如果你不将自己的想法包装成至少与人工智能相关的内容,就很难找到愿意倾听你意见的人。
这个小型会议接受了关于“人工智能”和“认知科学”的论文——我视此为对“符号处理”之外的想法的邀请。所以我提交了我的第一篇论文,并且被接受了!论文展示了一种处理自然语言的神经网络方法。我们这个领域的许多人称这种神经网络研究为“连接主义”,但现在这种研究,如前所述,会被标记为“深度学习”(DL)——尽管我的初期研究并不深……只有三层!现代 DL 系统可以由数百层组成。
我的论文在会议上被接受了,我于 1990 年在伊利诺伊州的卡本代尔进行了展示。后来,会议组织者 John Dinsmore 邀请我提交论文的一个版本用于他正在编辑的一本书。我觉得自己无法独立完成论文,于是邀请了两位研究生朋友(Lisa Meeden 和 Jim Marshall)加入我。他们同意了,我们最终在书中完成了一章。这本书的标题是“符号与连接主义范式:弥合差距”。我们的论文很契合书的主题。我们将论文标题为“探索符号/亚符号连续体:RAAM 的案例研究”。令我高兴的是,这本书聚焦于这两种人工智能方法之间的分裂。我认为这个领域至今仍在挣扎于这种分歧。
关于我初期研究的更多内容,我会稍后再说。现在我想谈谈这个领域如何处理“嵌入”的可视化。首先,我们当时并没有称这些向量为“嵌入”。大多数研究使用了“隐层表示”这样的术语。这包括了连接主义系统为解决问题而学习的任何内部表示。按照我们当时的定义,有三种层:“输入”(在这里你插入数据集)、“输出”(在这里你放置期望的输出或“目标”)以及其他所有层——即“隐层”。隐层是网络激活在输入和输出之间流动的地方。隐层激活通常是高维的,并且是网络学习到的“概念”的表示。
像今天一样,视觉化这些高维向量被认为有助于深入理解这些系统的工作原理及其常见故障。在我们书中的章节中,我们使用了三种类型的可视化:
-
所谓的“Hinton 图”
-
聚类图,或树状图
-
投影到二维空间
第一种方法是 Hinton 和 Shallice 在 1991 年 使用的一个新创意。(这就是我们今天所知道的 Geoffrey Hinton。更多内容将在未来的文章中介绍)。这个图表是一个简单的想法,实用性有限。基本想法是,激活、权重或任何类型的数值数据可以用盒子表示:白色盒子(通常表示正数)和黑色盒子(通常表示负数)。此外,盒子的大小表示相对于模拟神经元中的最大值和最小值的值的大小。
这是我们论文中的表示,显示了网络隐藏层中的平均“嵌入”作为对网络呈现的单词的表示:
我们论文中的图 10。
Hinton 图确实有助于可视化数据中的模式。但它们并不真正有助于理解表示之间的关系,也无助于在维度数目大大增加时的理解。现代嵌入可以有数千维。
为了帮助解决这些问题,我们转向第二种方法:聚类图或 树状图。这些图表显示了任意两个模式之间的距离(无论如何定义)作为一个层次树。以下是我们论文中使用欧几里得距离的一个示例:
我们论文中的图 9。
这与 Hinton 图中显示的信息相同,但格式更加实用。在这里,我们可以看到个别模式之间以及整体模式之间的内部关系。请注意,垂直排序无关紧要:分支点的水平位置是图表的有意义方面。
在上面的树状图中,我们手动构建了整体图像,基于程序计算出的树簇。今天,有自动构建这种树和图像的方法。然而,当模式的数量远超过几十个时,图表可能变得难以理解。这是今天由 matplotlib 制作的一个示例。你可以在这里了解更多关于 API 的信息:matplotlib 树状图。
现代树状图,包含大量模式。图像由作者制作。
最后,我们来到了最后一种方法,也就是今天主要使用的方法:投影方法。这种方法使用算法找到一种将嵌入的维度数减少到更容易被人类理解的数量(例如,2 或 3 维)并绘制为散点图的方法。
在 1990 年,投影高维数据到较小维度集的主要方法是主成分分析(简称 PCA)。维度降维是一个活跃的研究领域,目前仍在不断开发新的方法。
目前最常用的降维算法包括:
-
PCA
-
t-SNE
-
UMAP
哪种方法最好?这实际上取决于数据的细节以及你创建维度降维的目标。
PCA 可能是总体上最好的方法,因为它是确定性的,并且允许你从高维空间创建到降维空间的映射。这对于在一个数据集上训练,然后检查测试数据集在学习空间中的投影非常有用。然而,PCA 可能会受到未缩放数据的影响,并可能导致一个“点球”,这对结构模式的洞察较少。
t-SNE,即 t-分布随机邻域嵌入,由 Roweis 和 Hinton(是的,就是那个 Hinton)于 2002 年创建。这是一种学习型投影,可以利用未缩放的数据。然而,t-SNE 的一个缺点是它不会创建映射,而只是一个用于寻找聚类的学习方法。也就是说,与具有**Projection.fit()和Projection.transform()**方法的其他算法不同,t-SNE 只能进行拟合。(有些实现,如openTSNE,提供了转换映射。然而,openTSNE 似乎与其他算法非常不同,速度较慢,支持度也低于其他形式。)
最后,还有 UMAP,即均匀流形近似与投影。该方法由McInnes 和 Healy 于 2018 年创建。对于许多高维空间,这可能是最佳的折中方案,因为它计算开销相对较小,但能够在降维中保留重要的表示结构。
这是一个将维度降维算法应用于 sklearn 中可用的未缩放乳腺癌数据的示例:
三种投影方法的降维示例,包括 PCA、t-SNE 和 UMAP。图像由作者制作。
你可以自己测试维度降维算法,以找到适合你用例的最佳方法,并使用Kangas DataGrid创建如上所示的图像。
如前所述,维度缩减仍然是一个活跃的研究领域。我完全期待在这一领域看到持续的改进,包括可视化信息在深度学习网络中的流动。以下是我们书籍章节中的一个最终示例,展示了激活在我们模型的表示空间中的流动:
图 7 来自我们的论文。神经网络解码部分中单步的隐藏层激活。
对人工智能、机器学习和数据科学中的想法来源感兴趣吗?请考虑点赞和订阅。告诉我你感兴趣的内容!
纽约市的可视化
原文:
towardsdatascience.com/visualizations-of-new-york-city-f7b4772e9518
使用 Python 和 Plotly 让 NYC 开放数据生动起来
·发表于 Towards Data Science ·阅读时间 9 分钟·2023 年 8 月 18 日
–
图片由 Fabien BELLANGER 提供,来源于 Unsplash
纽约市的 开放数据平台 是一个令人惊叹的信息来源。城市收集和生成的所有公共数据都 依法 通过该门户提供,并且对公众免费使用。
数据集涵盖了交通、住房和机动车事故等信息,甚至包括中央公园松鼠普查和公园护林员报告的攻击性乌龟遭遇情况。
像这些地理、基础设施和社会学数据集代表了现实世界的过程和事件。即使你与纽约市或城市地区没有联系,或者对它们兴趣不大,它们也为你提供了一个机会,让你处理的数据更接近于你在专业角色中会遇到的数据,而不像 MNIST 或泰坦尼克号乘客那样。更好的是,它们几乎一样容易获取。
我们将演示这些数据集的使用有多么简单,并在此过程中构建一些有趣的可视化。
为了尽可能简洁地保持代码块,这里是本文中所有代码所需的模块:
import folium
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import requests
from scipy.stats import gaussian_kde
import seaborn as sns
from shapely.geometry import Point, shape, box, Polygon
如果你想自己复制任何内容,确保它们已经安装。
建筑物足迹
这是我最喜欢玩的数据集之一。数据包括 NYC 大多数建筑物的足迹多边形、年龄和高度。
我们将从数据提取开始,分开于可视化代码,因为我们使用此数据集制作了几个不同的可视化。
# Pull data
api_endpoint = 'https://data.cityofnewyork.us/resource/qb5r-6dgf.json'
limit = 1000 # Number of rows per request
offset = 0 # Starting offset
data_frames = [] # List to hold chunks of data
# Loop to fetch data iteratively
# while offset <= 100000: # uncomment this and comment while True to fetch a
# sample much faster
while True: # while True will take a long time but gets all the data
url = f"{api_endpoint}?$limit={limit}&$offset={offset}"
chunk = pd.read_json(url)
if chunk.empty:
break # Stop the loop when no more data is returned
data_frames.append(chunk)
offset += limit
# Concatenate all chunks into a single DataFrame
data = pd.concat(data_frames, ignore_index=True)
# Convert the 'the_geom' column from a dictionary to a Shapely geometry object
data['geometry'] = data['the_geom'].apply(lambda x: shape(x))
# Convert the Pandas DataFrame to a GeoDataFrame
gdf = gpd.GeoDataFrame(data, geometry='geometry', crs="EPSG:4326")
# Convert 'MultiPolygon' to representative points for visualization
gdf['centroid'] = gdf['geometry'].centroid.to_crs(epsg=3395).centroid.to_crs(epsg=4326)
# Get rid of columns we don't need anymore
keep_cols = ['cnstrct_yr', 'heightroof', 'geometry', 'centroid']
gdf = gdf[keep_cols]
这为我们提供了一个工作数据集,长得像这样。
几何列是建筑轮廓的多边形,重心列是轮廓中心的单一点,即建筑位置的单一经纬度。现在我们可以深入到有趣的部分了。
新旧建筑的对比
此时,城市已成为旧褐石房、密集的独栋住宅和排屋、方块形公寓楼以及俯瞰河流的玻璃塔楼的混合体。
比如我们想找一个历史悠久的社区居住,可能希望有很多老旧建筑来增加一些历史魅力。在许多美国城市,尤其是在东北部,1930 年代及之前的建筑被称为“战前建筑”或口语中的“prewars”。
为了找到适合我们需求的社区,我们可以使用散点图在地图上标记每栋建筑的重心位置,并使用 KDE 图突出显示战前建筑的高密度。
# Create the bounding box from the provided corner points
bounding_box = box(-74.0294, 40.685, -73.91695, 40.742)
# Filter the GeoDataFrame using the bounding polygon
gdf = gdf[gdf['centroid'].within(bounding_box)]
# Create a new column for building decade
gdf['decade'] = (gdf['cnstrct_yr'] // 10) * 10
# Remove rows where 'cnstrct_yr' is NaN
gdf = gdf[gdf['cnstrct_yr'].notna()]
# Get unique decades
unique_decades = sorted(gdf['decade'].unique())
# Use the Cividis colorscale and split it for our unique decades
colorscale = px.colors.sequential.Cividis
num_decades = len(unique_decades)
colors = [colorscale[i * len(colorscale) // num_decades] for i in range(num_decades)]
color_map = dict(zip(unique_decades, colors))
# Filter the data for buildings built in the 1930s and earlier
old_buildings = gdf[gdf['decade'] <= 1930]
# Create a new figure for better control
fig = go.Figure()
# Add the traces for each decade
for decade, color in color_map.items():
subset = gdf[gdf['decade'] == decade]
# Add the original trace with showlegend set to False
fig.add_trace(go.Scattermapbox(
lat=subset['centroid'].y,
lon=subset['centroid'].x,
mode='markers',
marker=go.scattermapbox.Marker(
size=3,
color=color,
opacity=0.8
),
text=decade,
name=str(int(decade)),
hoverinfo='none',
showlegend=False
))
# Add a dummy trace with larger markers for the legend
# placed outside the visible map
fig.add_trace(go.Scattermapbox(
lat=[90], # Latitude outside the visible map
lon=[0], # Longitude outside the visible map
mode='markers',
marker=go.scattermapbox.Marker(
size=10,
color=color,
opacity=1
),
legendgroup=str(int(decade)),
showlegend=True,
name=str(int(decade)),
hoverinfo='none'
))
# Add heatmap for older buildings
fig.add_trace(go.Densitymapbox(
lat=old_buildings['centroid'].y,
lon=old_buildings['centroid'].x,
radius=4,
colorscale="Greens",
opacity=1,
name="Density of Prewar Buildings",
showlegend=True,
zmax=3,
zmin=0,
showscale=False
))
fig.update_layout(
title='Buildings by Decade with Density Underlay for Prewar Buildings',
autosize=True,
mapbox=dict(
accesstoken=None,
bearing=0,
center=dict(lat=40.71359, lon=-73.97216),
pitch=0,
zoom=12.6,
style='carto-positron'
),
height=800,
width=1200,
legend=dict(tracegroupgap=0)
)
# Display the map
fig.show()
显示战前建筑数量较多的街道和社区。图表由 Plotly 创建。
如果你对纽约市熟悉,这些数据中的一些结果并不令人惊讶,但有些可能会让人感到意外。科布尔希尔和西村的经典褐石房排是显而易见的。但是,我实际上没有预料到格林点和威廉斯堡(布鲁克林北端)有如此多的老建筑。
建筑尺寸
由于数据集中有建筑轮廓和屋顶高度,我们可以计算建筑体积。然后,我们可以通过行政区可视化建筑的平均大小以进行比较。
为了实现这一点,我们将使用建筑轮廓的几何形状和屋顶高度来计算数据中每栋建筑的体积。我们将使用每个轮廓的重心来确定其所在的行政区。
# ...following from data pull code block
# Define borough bounding boxes
# These are very loose bounds and a more thorough analysis should use a higher
# precision polygon.
boroughs = {
"Manhattan": box(-74.02, 40.70, -73.91, 40.88),
"Bronx": box(-73.93, 40.80, -73.79, 40.92),
"Brooklyn": box(-74.05, 40.57, -73.85, 40.74),
"Queens": box(-73.94, 40.54, -73.70, 40.80),
"Staten Island": box(-74.26, 40.50, -74.03, 40.65)
}
# Assign borough to each building based on its centroid
def assign_borough(centroid):
for borough, bbox in boroughs.items():
if bbox.contains(centroid):
return borough
return None
# Assuming the gdf variable already contains your data
gdf['borough'] = gdf['centroid'].apply(assign_borough)
# Calculate building volume using footprint area and height
gdf['volume'] = gdf['geometry'].area * gdf['heightroof']
# Compute average volume by borough
avg_volume_by_borough = gdf.groupby('borough')['volume'].median()
# Create 3D bar shapes using surface plots
def create_3d_bar(x, y, z, dx, dy, dz):
# Define vertices of the bar
x_data = [[x, x, x+dx, x+dx, x], [x, x, x+dx, x+dx, x]]
y_data = [[y, y+dy, y+dy, y, y], [y, y+dy, y+dy, y, y]]
z_data = [[z, z, z, z, z], [z+dz, z+dz, z+dz, z+dz, z+dz]]
return go.Surface(
x=x_data,
y=y_data,
z=z_data,
colorscale=[[0, 'blue'], [1, 'blue']],
showscale=False,
opacity=0.5
)
# Define bar dimensions
dx = 0.4
dy = 0.4
# Create figure
fig = go.Figure()
# Add bars to the figure
for i, borough in enumerate(avg_volume_by_borough.index):
fig.add_trace(create_3d_bar(i, 0, 0, dx, dy, avg_volume_by_borough[borough]))
# Define the layout with adjusted aspect ratio for wider chart area
fig.update_layout(
title='Average Building Volume by Borough',
scene=dict(
xaxis=dict(
title='Borough',
tickvals=list(range(len(avg_volume_by_borough))),
ticktext=avg_volume_by_borough.index
),
yaxis=dict(title='', visible=False),
zaxis=dict(title='Average Building Volume (m³)'),
aspectratio=dict(x=3, y=2, z=1.5) # Adjusting the aspect ratio for wider x-axis
),
margin=dict(t=40, b=40, l=60, r=60)
)
fig.show()
各区建筑体积的体积柱状图。图表由 Plotly 创建。
曼哈顿的建筑更大这一点并不令人惊讶。然而,你可能会预期其他三个区之间存在更大的差异,尤其是布鲁克林的塔楼俯瞰东河与斯塔滕岛明显的纽约市郊外风貌之间的差异。
四个外区都有大量低密度住房。特别是皇后区,其面积巨大,比第二大区布鲁克林大 56%。
我们可以在建筑尺寸分布的箱形图中看到这一点。请注意体积是对数化的,以更好地展示外区与曼哈顿大量高楼之间的差异。
皇后区的小型独栋住宅数量较大,相较于斯塔滕岛尤为明显。
WiFi 热点
纽约市在全市范围内运营 wifi 热点,其位置可通过开放数据门户访问。这些只是市政府运营的热点,所以像星巴克这样的地方不包括在内。
我们可以直接从门户网站读取 JSON 文件并创建位置地图。
# Define the URL
url = "https://data.cityofnewyork.us/resource/yjub-udmw.json"
# Send a GET request
response = requests.get(url)
# Load the response into a JSON
data = response.json()
# Convert the JSON data into a DataFrame
df = pd.DataFrame(data)
# Convert lat and lon columns to float
df['latitude'] = df['latitude'].astype(float)
df['longitude'] = df['longitude'].astype(float)
# Map the borough codes to borough names
borough_dict = {'1': 'Manhattan', '2': 'Bronx', '3': 'Brooklyn', '4': 'Queens', '5': 'Staten Island'}
df['borough'] = df['borough'].map(borough_dict)
# Replace the 'token' with your own Mapbox access token
px.set_mapbox_access_token('token')
fig = px.density_mapbox(
df,
lat='latitude',
lon='longitude',
zoom=10,
mapbox_style="carto-positron",
title="Distribution of WiFi Hotspots in NYC",
radius=6
)
fig.update_layout(
height=800,
width=1200
)
fig.show()
纽约市运营的 wifi 热点的位置。图表由 Plotly 创建。
纽约市的大多数地铁站都有免费 wifi,这在地图上显示得非常清晰。中央公园两侧延伸出的高密度热点区域大多是地铁站。
你还可以通过寻找排列成不自然直线的地点,清楚地看到两条主要的地铁线路通向皇后区和布鲁克林。
松鼠普查
松鼠普查(www.thesquirrelcensus.com/
)是一个科学、设计和讲故事的项目,专注于东部灰松鼠(Sciurus carolinensis)。他们统计松鼠数量并将结果公之于众。
这些数据包含了 3,023 次观察的松鼠数据,包括位置坐标、年龄、主要和次要毛色、高度、活动、通讯以及松鼠与人类之间的互动。
现在让我们提出一个有趣的问题。
中央公园中最吵的松鼠在哪里?
数据中有三个字段表示松鼠发出的不同噪音/叫声:‘kuks’、‘quaas’和‘moans’。我们将任何噪音视为吵闹,并创建一个密度图以显示吵闹的松鼠聚集在哪里。
# Pull data
data_url = 'https://data.cityofnewyork.us/api/views/vfnx-vebw/rows.csv?accessType=DOWNLOAD'
squirrels = pd.read_csv(
data_url,
usecols=['X', 'Y', 'Kuks', 'Quaas', 'Moans']
)
# Create column denoting that the squirrel made any kind of noise
squirrels['noisy'] = squirrels[['Kuks', 'Quaas', 'Moans']].any(axis=1)
# Filter out the quiet squirrels
noisy_squirrels = squirrels[squirrels['noisy']]
# Convert noisy column to integer
noisy_squirrels['noisy'] = noisy_squirrels['noisy'].astype(int)
# Create the density heatmap
fig = px.density_mapbox(
noisy_squirrels, lat='Y', lon='X', z='noisy', radius=50,
center=dict(lat=40.783, lon=-73.969), # Center coordinates for Central Park
zoom=13,
mapbox_style="stamen-terrain",
#mapbox_style="stamen-watercolor",
color_continuous_scale=["white", "orange", "red"],
range_color=[0, 5], # Adjusting the range for color scale
)
# Set the bearing to orient Central Park horizontally
fig.update_layout(
mapbox_bearing=0,
height=700,
width=1000,
title='Density of Noisy Squirrel Observations',
showlegend=False,
coloraxis_showscale=False
)
fig.show()
松鼠发出声音的观察密度图。图表由 Plotly 创建。
看起来上西区的松鼠有最多的发言权。
最后的说明
在 NYC Open Data 上还有大量数据可以深入探索,确保自己查看。大多数大城市也有自己的开放数据计划。值得注意的广泛数据计划包括洛杉矶、多伦多、伦敦和新加坡。
NYC Open Data 是公共领域数据,根据开放数据常见问题 “对开放数据的使用没有限制”,包括商业用途。有关详细信息,请参阅使用条款。
本帖中所有代码均可在GitHub上获得,我已经将 MIT 许可证附加到仓库中,以便你可以自由使用其中的任何内容。
使用 Matplotlib 可视化数据范围
原文:
towardsdatascience.com/visualize-data-ranges-with-matplotlib-df815363a619
基准测试 NOAA 的飓风展望
·发表于数据科学进阶 ·阅读时间 10 分钟·2023 年 9 月 26 日
–
莱昂纳多 AI DreamShaper_v7 模型的太空飓风
绘制离散数据很简单;表示范围数据则更复杂。幸运的是,Python 的 matplotlib 库提供了一个内置函数fill_between()
,可以轻松可视化数据范围。在这个快速成功的数据科学项目中,我们将利用它来基准测试国家海洋和大气管理局的年度飓风展望。
数据集
每年 5 月,NOAA 发布其“大西洋飓风展望”报告,涵盖 6 月至 11 月的飓风季节。这些展望包括对命名风暴、飓风和主要飓风(定义为三级及以上)的预测范围。你可以在这里找到 2021 年的示例报告[1]。NOAA/国家气象局的数据由美国政府提供,作为开放数据,可以自由使用。
为了基准测试这些预测的准确性,我们将使用维基百科提供的年度飓风季节总结。这些总结提供了每年的实际风暴和飓风数量。你可以在这里找到 2021 季节的条目[2]。维基百科页面在CC BY-SA 4.0许可下提供。
维基百科还包括拉尼娜和厄尔尼诺事件的列表[3][4]。这些代表了每隔几年在太平洋发生的天气模式。在拉尼娜年,东太平洋的水温低于正常水平,导致其上方的空气变冷。厄尔尼诺年则相反。
拉尼娜现象有利于大西洋盆地飓风活动的增强,而厄尔尼诺则抑制飓风发展 [5]。为此,我们还将对这些事件进行颜色编码。
为了方便,我已经将 2001–2022 年的所有信息汇总并存储为 CSV 文件,保存在这个Gist中。
NOAA 每年八月都会发布更新的飓风预测,因此在选择数据和引用预测时需要小心。我们将使用五月的预测。
安装库
我们将使用 pandas 进行数据处理,使用 matplotlib 进行绘图。可以通过以下任一方式进行安装:
conda install matplotlib pandas
或
pip install matplotlib pandas
代码
以下代码在 JupyterLab 中编写,并按单元描述。
导入模块
除了进行数据分析和绘图,我们还将制作一个自定义标记来表示飓风。为此,我们需要导入 NumPy(Python 的数值分析包)和一个用于处理折线的 matplotlib 模块,称为mpath
。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.path as mpath
import pandas as pd
加载数据集
CSV 文件包含了预测飓风(H)和主要飓风(MH)的低值和高值。它还包括实际的飓风和主要飓风数量,以及是否属于拉尼娜或厄尔尼诺事件。过渡年被标记为“弱事件”。
df = pd.read_csv('https://bit.ly/44YgahT')
df.head(3)
定义绘制飓风标记的函数
虽然我们可以使用一个简单的圆圈来在散点图上标记实际的飓风数量,但经典的飓风图标岂不是看起来更好?
不幸的是,matplotlib 没有自带飓风标记。然而,在 Stack Overflow 上提供了绘制飓风标记的代码,我在下面重复了这段代码(Stack Overflow 内容是cc-wiki 许可的) [6]。
这个函数使用了 matplotlib 的mpath
模块,该模块返回一个[<class 'matplotlib.path.Path'>](https://matplotlib.org/stable/api/path_api.html)
对象,表示一系列的线段和曲线段。该代码如何工作对于这个项目并不重要,但如果你想看到详细的解释,可以访问代码片段开始处的 Stack Overflow 链接。
# The following code was adapted from Stack Overflow:
# https://stackoverflow.com/questions/44726675/custom-markers-using-python-matplotlib
# Asked by: https://stackoverflow.com/users/5689281/kushal
# Answered by: https://stackoverflow.com/users/4124317/importanceofbeingernest
def get_hurricane_symbol():
"""Return a hurricane warning symbol as a matplotlib path."""
# Create a numpy array for the symbol's coordinates and codes:
coordinates = np.array([[2.444, 7.553],
[0.513, 7.046],
[-1.243, 5.433],
[-2.353, 2.975],
[-2.578, 0.092],
[-2.075, -1.795],
[-0.336, -2.870],
[2.609, -2.016]])
# Shift the x-coordinates:
coordinates[:, 0] -= 0.098
# Define path codes:
codes = [1] + [2] * (len(coordinates) - 2) + [2]
# Duplicate and reverse the coordinates:
coordinates = np.append(coordinates, -coordinates[::-1], axis=0)
# Duplicate the codes:
codes += codes
# Create and return the matplotlib path:
return mpath.Path(3 * coordinates, codes, closed=False)
绘制实际飓风与预测飓风
以下代码使用 matplotlib 的[fill_between()](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.fill_between.html)
方法来捕捉 NOAA 对每年的飓风预测数量。它需要 DataFrame 列名作为x
参数,y1
的最小值和y2
的最大值。添加label
参数可确保范围阴影会在图例中进行引用。
# Call the function to build the hurricane marker:
symbol = get_hurricane_symbol()
# Initialize the figure:
plt.figure(figsize=(10, 4))
# Plot the actual number of hurricanes per year:
plt.plot(df.Year, df['Actual H'],
label='Actual Value',
marker=symbol,
markersize=17,
c='darkblue',
linestyle='None',
lw=1)
# Shade NOAA's predicted range of hurricanes for each year:
plt.fill_between(x=df.Year,
y1=df['Predicted H Low'],
y2=df['Predicted H High'],
alpha=0.3,
label='Predicted Range')
plt.xlabel('Year')
plt.ylabel('Number of Hurricanes')
plt.legend(loc='lower right')
plt.grid(True, c='lightgrey', alpha=0.5)
plt.title('Actual Number of Atlantic Hurricanes vs. \
NOAA May Prediction (2001-2022)');
# Optional code to save the figure:
# plt.savefig('range_plot.png', bbox_inches='tight', dpi=600)
范围图(作者)
这个简单而优雅的图表充满了有用的信息。例如,在过去的 22 年中,实际的飓风数量有11 次落在预测范围内。这与掷硬币的准确性相同。最近,NOAA 开始使用更广泛的范围,这增加了准确性但降低了精确度。
改变填充样式
我非常喜欢之前的预测范围填充样式,但确实还有其他选择。在这个例子中,我们将step
参数传递给fill_between()
方法。现在,我们得到的是离散的垂直条,而不是连续的多边形。
plt.figure(figsize=(10, 4))
plt.plot(df.Year, df['Actual H'],
label='Actual Value',
marker=symbol,
markersize=17,
c='darkblue',
linestyle='None',
lw=1)
plt.fill_between(x=df.Year,
y1=df['Predicted H Low'],
y2=df['Predicted H High'],
step='mid',
alpha=0.3,
label='Predicted Range')
plt.xlabel('Year')
plt.ylabel('Number of Hurricanes')
plt.legend(loc='lower right')
plt.grid(True, c='lightgrey', alpha=0.5)
plt.title('Actual Number of Atlantic Hurricanes vs. \
NOAA May Prediction (2001-2022)');
使用“step”参数的范围图(作者)
添加厄尔尼诺和拉尼娜事件
为了评估厄尔尼诺和拉尼娜事件对飓风数量和强度的影响,让我们利用数据框中的“事件”列。
首先,我们需要制作一个将事件映射到颜色的字典。由于拉尼娜代表冷却事件,我们将使用蓝色。厄尔尼诺的暖事件将是红色,而弱事件将是无特征的灰色。
我们将在图形标题下方添加一个单独的自定义图例。注意使用$\u25CF$
来绘制圆圈。这是来自实用的STIX 字体集合的一个符号。
# Plot the predicted ranges and color the actual values by event.
# Define a dictionary to map text colors to matplotlib colors:
color_mapping = {'Nina': 'blue',
'Nino': 'red',
'Weak Event': 'grey'}
# Map the Event column to colors. Use black if x not found:
df['colors_mapped'] = df['Event'].apply(lambda x: color_mapping.get(x, 'k'))
plt.figure(figsize=(10, 4))
plt.scatter(df.Year, df['Actual H'],
label='Actual Value',
marker=symbol,
s=300,
c=df.colors_mapped,
linestyle='None',
lw=1)
plt.fill_between(x=df.Year,
y1=df['Predicted H Low'],
y2=df['Predicted H High'],
alpha=0.3,
label='Predicted Range')
plt.xlabel('Year')
plt.ylabel('Number of Hurricanes')
plt.legend(loc='lower right')
plt.grid(True, c='lightgrey', alpha=0.5)
# Add event legend as title:
plt.suptitle('Actual Number of Atlantic Hurricanes vs. NOAA May Prediction (2001-2022)')
plt.figtext(0.4, 0.9, '$\u25CF$ La Nina', fontsize='medium', c='b', ha ='right')
plt.figtext(0.5, 0.9, '$\u25CF$ El Nino', fontsize='medium', c='r', ha ='center')
plt.figtext(0.6, 0.9, '$\u25CF$ Weak Event', fontsize='medium', c='grey', ha ='left');
按天气事件着色的范围图(作者)
这些结果似乎支持了厄尔尼诺事件抑制大西洋飓风形成的理论,至少相对于拉尼娜事件。为了查看它们是否也影响飓风强度,让我们绘制主要飓风数据。
绘制主要飓风
主要飓风定义为 3 级或更高级别的飓风。以下代码更新了这些值的图示。
plt.figure(figsize=(10, 4))
plt.scatter(df.Year, df['Actual MH'],
label='Actual Value',
marker=symbol, s=300,
c=df.colors_mapped,
linestyle='None',
lw=1)
plt.fill_between(x=df.Year,
y1=df['Predicted MH Low'],
y2=df['Predicted MH High'],
alpha=0.3,
label='Predicted Range')
plt.xlabel('Year')
plt.ylabel('Number of Major Hurricanes (Cat 3+)')
plt.legend(loc='lower right')
plt.grid(True, c='lightgrey', alpha=0.5)
# Add event legend as title:
plt.suptitle('Actual Number of Major Atlantic Hurricanes vs. NOAA May Prediction (2001-2022)')
plt.figtext(0.4, 0.9, '$\u25CF$ La Nina', fontsize='medium', c='b', ha ='right')
plt.figtext(0.5, 0.9, '$\u25CF$ El Nino', fontsize='medium', c='r', ha ='center')
plt.figtext(0.6, 0.9, '$\u25CF$ Weak Event', fontsize='medium', c='grey', ha ='left');
按天气事件颜色编码的主要飓风范围图(作者)
除了 2004 年,某些来源将其归类为弱事件,这张图表支持了在厄尔尼诺事件期间飓风形成被抑制的观点[7]。对于主要飓风,预测准确性也稍好,22 个中有 13 个落在预测范围内。
使用垂直线绘制范围
另一种绘制范围的方法是使用 matplotlib 的vlines()
方法来绘制垂直线。这是fill_between()
方法的一个有吸引力的替代方案,尽管它更费力且不会自动将范围包含在图例中。
# Redraw plot with vertical lines for ranges:
plt.figure(figsize=(10, 4))
# Use a scatter plot for actual values:
plt.scatter(df.index, df['Actual H'],
label='Actual Value',
marker=symbol,
c='darkblue',
s=350)
# Draw vertical lines for the predicted ranges:
for i, row in df.iterrows():
plt.vlines(x=i,
ymin=row['Predicted H Low'],
ymax=row['Predicted H High'],
alpha=0.4,
lw=6,
zorder=0)
x = range(len(df))
plt.xticks(x, df.Year, rotation=90)
plt.xlabel('Year')
plt.ylabel('Number of Hurricanes')
plt.legend(loc='lower right')
plt.grid(True, color='lightgray', alpha=0.5)
plt.title('Actual Number of Atlantic Hurricanes vs. NOAA May Prediction');
使用垂直线表示范围的范围图(作者)
评估大西洋多年代际振荡
我们现在已经涵盖了fill_between()
方法,但既然我们手头有所有这些数据,不妨花点时间研究一个关于飓风形成的有趣理论,这涉及到大西洋多年代际振荡(AMO)[8]。
AMO 是由北大西洋海表温度的几十年变率定义的特征。关于 AMO 知之甚少;它可能代表一个持续的周期性气候驱动因素或只是一个短暂特征[9]。
AMO 指数是通过从全球平均海表温度(SST)异常中减去北大西洋SST 异常来计算的[9]。当 AMO 指数较高时,海表温度通常较温暖,可能会促使飓风活动和强度增加。
由于这是一个长波长现象,我们需要一个从 1920 年左右开始计算飓风的数据库。我已经记录了维基百科在这一时间段的飓风列表,并将其存储在这个Gist中。
应注意,在使用飞机(20 世纪 40 年代中期)和卫星数据(20 世纪 60 年代中期)之前的风暴计数不够可靠。例如,1886 年到 1910 年的计数估算被认为有每年零到四场风暴的低估偏差[10]。
在下一个图中,AMO 指数的边界取自维基百科和NOAA [8][11]。
# Load the 1920-2022 hurricane dataset:
df = pd.read_csv('https://bit.ly/3sZnvQX')
# Plot major hurricanes per year with regression line and AMO shading:
plt.figure(figsize=(10, 4))
plt.plot(df.Year, df.MH,
label='Actual Value',
marker=symbol,
markersize=17,
c='darkblue',
linestyle='None',
lw=1)
plt.xlabel('Year')
plt.xticks(range(1920, 2021, 10))
plt.ylabel('Number of Major Hurricanes (Cat 3+)')
plt.grid(True, c='lightgrey', alpha=0.5)
plt.title('Number of Major Atlantic Hurricanes by Year 1920-2022',
fontsize=18)
# Add a shaded span for AMO highs:
plt.text(1940, 6.5, 'AMO High', c='firebrick')
plt.axvspan(1926, 1964,
color='red',
alpha=0.2)
plt.text(2005, 6.5, 'AMO High', c='firebrick')
plt.axvspan(1995, 2022,
color='red',
alpha=0.2)
# Calculate m (slope) and b (intercept) of linear regression line:
m, b = np.polyfit(df.Year, df.MH, 1)
# Add linear regression line to plot:
plt.plot(df.Year, m*df.Year+b, c='darkblue', ls=':');
每年主要飓风数量,包括回归线和 AMO 指数高期(作者提供)
以下是以柱状图形式呈现的相同数据:
包含 AMO 指数高期的主要飓风柱状图(作者提供)
这是这段时间内所有大西洋飓风的散点图。AMO 效应在风暴频率上的表现不太明显。
每年总飓风数量,包括回归线和 AMO 指数高期(作者提供)
尽管科学家认识到 AMO 指数与主要飓风数量之间的明显关系,但目前的数据还不足以得出确切结论。正如你可能预料的那样,最近 AMO 高期主要飓风增加的最流行解释是人为气候变化。
总结
matplotlib 的fill_between()
方法是显示图上范围值的便捷方式。在这个项目中,我们用它来展示 NOAA 的年度飓风预测与实际结果的对比。此外,我们还使用了 matplotlib 的mpath
模块绘制自定义标记来表示飓风。最终结果是一个吸引人且易于解析的信息图。
我们还在图中添加了厄尔尼诺、拉尼娜和 AMO 事件。结果支持了既有观察结果,即厄尔尼诺似乎抑制了大西洋飓风,而 AMO 指数高期事件似乎促进了它们。
引用
-
气候预测中心互联网团队, 2001, “NOAA 2021 年大西洋飓风季节展望,” 气候预测中心 — 大西洋飓风展望 (noaa.gov)
-
维基百科贡献者,“2021 年大西洋飓风季节,” 维基百科,自由百科全书,
en.wikipedia.org/w/index.php?title=2021_Atlantic_hurricane_season&oldid=1175731221
(访问日期:2023 年 9 月 19 日). -
维基百科贡献者,“厄尔尼诺,” 维基百科,自由百科全书,
en.wikipedia.org/w/index.php?title=El_Ni%C3%B1o&oldid=1174548902
(访问日期:2023 年 9 月 19 日). -
维基百科贡献者,“拉尼娜,” 维基百科,自由百科全书,
en.wikipedia.org/w/index.php?title=La_Ni%C3%B1a&oldid=1174382856
(访问日期:2023 年 9 月 19 日). -
Bell, Gerry, 2014, “厄尔尼诺和拉尼娜对飓风季节的影响,” NOAA 气候.gov, Impacts of El Niño and La Niña on the hurricane season | NOAA Climate.gov.
-
ImportanceOfBeingErnest, “使用 matplotlib 的自定义标记,” Stack Overflow, 2017 年 6 月 24 日, Custom markers using Python (matplotlib) — Stack Overflow (访问日期:2023 年 9 月 19 日).
-
Null, Jan, 2023, “厄尔尼诺和拉尼娜年份及强度,” 金门大桥天气服务, El Niño and La Niña Years and Intensities (ggweather.com) (访问日期:2023 年 9 月 19 日).
-
维基百科贡献者,“大西洋多年代际振荡,” 维基百科,自由百科全书,
en.wikipedia.org/w/index.php?title=Atlantic_multidecadal_oscillation&oldid=1175329341
(访问日期:2023 年 9 月 19 日). -
Knudsen, M., Seidenkrantz, MS., Jacobsen, B. 等, “追踪过去 8,000 年的大西洋多年代际振荡,” 自然通讯 2, 178 (2011).
doi.org/10.1038/ncomms1186
. -
维基百科贡献者,“大西洋飓风记录列表,” 维基百科,自由百科全书,
en.wikipedia.org/w/index.php?title=List_of_Atlantic_hurricane_records&oldid=1168214070
(访问日期:2023 年 9 月 19 日). -
NOAA, 2017 年,“大西洋多年代振荡低频气候模式”,大西洋海洋气象实验室,墨西哥湾 ESR (noaa.gov).
谢谢!
感谢阅读。我的目标是帮助你提升 Python 技能,并且在过程中享受乐趣。请关注我,未来会有更多快速成功的数据科学项目。