TowardsDataScience 2023 博客中文翻译(二百八十四)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

使用扩展的 Databricks MLFlow 保障 MLOps 的安全

原文:towardsdatascience.com/secure-mlops-with-extended-databricks-mlflow-ee9b7310c5b3?source=collection_archive---------31-----------------------#2023-01-10

安全管理模型目标环境

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Luuk van der Velden

·

关注 发表在 Towards Data Science ·6 分钟阅读·2023 年 1 月 10 日

MLFlow 是一个开源的 Databricks 产品,支持机器学习模型生命周期的部分功能。它的模型注册表允许将模型版本以模型名称注册,以便于模型部署。我们希望使用一个 MLFlow 实例和一个 Databricks 工作区来支持多个部署目标(接受、预发布、生产等),同时为生产模型提供安全保障。我们扩展了 MLFlow 客户端,以安全的方式在一个模型注册表中管理多个环境。该客户端与 Databricks 权限协同工作,我们将展示 Azure terraform 代码片段。

github 仓库:环境 MLFlow 客户端存储库

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Aleksa Kalajdzic 拍摄的照片

1 为什么选择一个 Databricks 工作区?

安全的 Databricks 工作区部署在虚拟网络中,这要求控制平面和计算平面分别使用不同的子网。计算平面子网的 IP 范围限制了可以同时使用的并行计算节点的最大数量。由于公司范围内的 IP 范围大小限制,我们选择为一个 Databricks 工作区使用一个大的子网,而不是多个较小的子网绑定到多个 Databricks 工作区。因此,我们希望在一个 Databricks 工作区中管理多个逻辑环境(验收、预生产和生产)。

1.2 使用 Databricks 池进行扩展

Databricks 池允许在集群创建过程中重用闲置实例(虚拟机)。池提供了适中的启动和自动缩放速度的好处。池中的每个实例需要计算平面子网中的一个 IP。如果达到最大限制,下一个实例创建请求将失败,且没有优雅的错误处理或回退机制。Databricks 池的这个限制使得拥有一个大的子网对于你的活动至关重要,因为你随时可能达到最大限制,从而导致应用程序崩溃。

2 基于身份的安全

我们的安全策略基于身份,主要是 Azure 和 Databricks 用户的应用程序注册,背后由 Azure Active Directory (AAD) 用户支持。例如,我们在生产数据的相同 Databricks 工作区中运行验收测试,使用具有只读权限的独立身份,将生产数据复制到临时验收测试存储帐户。身份在 Azure 上非常方便,因为 Databricks 支持凭证透传,利用我们在身份上设置的所有 AAD 角色和组。我们还依赖于 Databricks 权限和组,因为我们在一个 Databricks 工作区中使用多个身份。

2.1 Databricks 组

我们的单个 Databricks 工作区中的每个逻辑环境都有一个名为 f”apps_{env_name}”(例如 apps_acc, apps_prod)的 Databricks 组,用于其应用程序主体,以及一个包含所有活动应用程序主体的组“apps_all”。这些组及其权限由 terraform 管理。

Databricks 组 terraform 摘录

3 环境 MLFlow 客户端

3.1 MLFlow 实验跟踪

MLFlow 实验存储被调整以支持多个逻辑环境,通过按环境管理存储位置。我们的解决方案分配了如“/experiments/acc”和“experiments/prod”的目录来存储实验数据。使用 Databricks 权限管理来授予相应 Databricks 组(在本例中为“apps_acc”和“apps_prod”)目录权限。这允许对每个逻辑环境的实验和模型进行安全日志记录,无需用户考虑。

MLFlow 实验 Databricks 权限 terraform 摘录

3.2 MLFlow 模型注册表

MLFlow 模型注册表是一个中央位置,用于注册模型和模型版本以供 ML 系统使用。在不同环境中的数据科学实验中记录和注册的模型最终会进入同一个模型注册表。MLFlow 模型注册表较难调整以支持多个逻辑环境。实际上,MLFlow 中的部署目标概念假设每个特定模型的版本可以具有不同的“阶段”。模型阶段是注册在 MLFlow 模型注册表中的模型版本的一个属性。它可以设置为“None”(无)、“Staging”(暂存)、“Production”(生产)和“Archived”(归档)。尽管有帮助,但模型阶段非常有限,因为我们不能为其定义自己的值,因此它不能完全支持我们定义各种逻辑环境的需求。我们确实使用它来管理与 Databricks 的模型版本权限,我们将在后文中描述。

模型命名

我们决定模型命名将作为区分任意数量逻辑环境的第一层。每个注册的模型名称都后缀带有环境标识符 f”{model_name}_{env_name}”。我们的 MLFlow 客户端在模型注册和检索过程中透明地管理这种命名,基于从系统环境变量获取的环境名称或传递给其构造函数的环境名称。与实验管理类似,由于我们在环境 MLFlow 客户端中的抽象,接口与原版 MLFlow 大致相同。

MLFlow 客户端摘录显示环境模型命名

模型权限

我们的目标是将生产模型与其他环境分离,并防止任何非生产主体注册或检索生产模型版本。如前所述,模型阶段可以设置为“None”(无)、“Staging”(暂存)、“Production”(生产)和“Archived”(归档)。Databricks 权限管理与模型阶段的值相关联。我们可以控制谁可以设置“Staging”和“Production”模型阶段值,以及谁可以使用“CAN_MANAGE_STAGING_VERSIONS”和“CAN_MANAGE_PRODUCTION_VERSIONS”权限管理具有这些模型阶段值的模型。“Production”阶段权限还允许主体访问“Staging”阶段模型版本并将其过渡到生产模型版本。

为了将生产模型与其他环境中的模型区分开来,我们在注册模型版本时自动分配模型阶段。所有非生产环境将模型阶段值分配为“Staging”。从生产环境注册的模型版本分配“Production”阶段值。我们利用分配给我们 Databricks 组的 Databricks 权限,安全地将我们的生产模型与其他逻辑环境分开管理。

Databricks MLFlow 模型权限 Azure terraform 摘录

3.3 汇总

除了对原生 MLFlow 客户端进行的抽象之外,我们还添加了一些封装各种操作的方法以方便使用。其中一个额外的方法是“log_model_helper”,它处理记录和注册模型版本以及设置适当模型阶段的各种步骤。

注册模型版本通常分为两个步骤,首先在实验运行期间记录一个模型,然后将记录的模型注册为已注册模型的模型版本。在实验运行期间记录模型会返回一个 ModelInfo 对象,其中包含一个指向本地工件位置的 model_uri。我们使用 model_uri 将模型版本注册到已注册模型名称下,并将其上传到 MLFlow 注册表。注册模型版本会返回一个 ModelVersion 对象,该对象告诉我们自动递增的模型版本和模型的 current_stage,创建后会始终是“None”。

在模型版本注册期间的第三步是根据逻辑环境将模型版本阶段从“None”过渡到“Staging”或“Production”。每个人都可以创建模型版本,但阶段转换受到 Databricks 权限的限制。这三个步骤都封装在“log_model_helper”方法中。使用这个辅助方法,我们可以假设所有已注册的模型版本都有环境感知的名称和适当的阶段值。

log_model_helper 摘录

3.4 测试我们的 MLFlow 客户端

为了维护我们自己的客户端,我们需要详细测试它,以检查是否符合我们的权限设计。对上游 MLFlow 客户端的任何更改也会同时进行测试,例如 MLFlow 模块的重构。我们在一个 PyTest 固定装置内启动一个本地 MLFlow 服务器,该装置的作用范围是整个测试会话。使用 Python tempfile 模块生成一个临时工件位置和一个临时 sqlite 数据库文件。我们测试会话的目标是记录和注册模型,并对其进行各种变更和检索。第一步是在空模型注册表中记录和注册一个模型版本。我们在 PyTest 固定装置内执行此操作,因为所有其他测试都依赖于它,尽管从技术上讲,它本身就是一个测试,因为它包括断言。

MLFlow 测试服务器固定装置,感谢 @Wessel Radstok 和 Rik Jongerius

模型注册的测试夹具

结论

我们扩展的环境 MLFlow 客户端允许我们将多个逻辑环境中的模型注册到同一个模型注册表中。它利用 Databricks 中的最小权限选项,在模型注册和部署检索期间安全地将开发环境与生产环境分开。此外,MLFlow API 在各环境中基本保持不变。

最初发布于 https://codebeez.nl

保护你的容器化模型和工作负载

原文:towardsdatascience.com/securing-your-containerised-models-and-workloads-3bff4d90a07b

切换到非根用户!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Jake Teo

·发表于 Towards Data Science ·阅读时间 8 分钟·2023 年 10 月 24 日

容器化现在是部署许多应用程序的事实上的方法,其中 Docker 是推动其采用的前沿软件。随着其流行,也带来了攻击风险的增加 [1]。因此,我们需要确保保护我们的 Docker 应用程序。实现这一目标的最基本方法是确保我们在容器内设置非根用户。

CONTENTS
========

Why use non-root?

What you can & cannot do as a default non-root user

The Four Scenarios
  1) Serve a model from host (Read Only)
  2) Run data processing pipelines (Write within Container)
  3) Libraries automatically writing files (Write within Container)
  4) Save trained models (Write to Host)

Summary

为什么要使用非根用户?

或者说,为什么不使用根用户?让我们以下面的虚拟架构为例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

黑客进入一个具有根权限的容器。图片由作者提供

安全通常被视为多层防御。如果攻击者设法进入容器,作为用户的权限将是第一道防线。如果容器用户被分配了根权限,攻击者就可以自由控制容器内的一切。凭借如此广泛的访问权限,攻击者还可以利用任何潜在的漏洞,并可能通过这些漏洞逃脱到主机,获得对所有连接系统的完全访问权限。后果非常严重,包括以下几点:

  • 检索存储的机密

  • 拦截并扰乱你的流量

  • 运行恶意服务,如加密挖矿

  • 访问任何连接的敏感服务,如数据库

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

攻击者可能通过根权限横向渗透你的基础设施服务。图片由作者提供

真是太可怕了!不过解决方案很简单,将你的容器改为非根用户!

在继续阅读本文之前,如果你对 Linux 权限和访问控制没有很好的理解,请查看我之前的 文章 [2]。

作为默认的非根用户,你可以做什么和不能做什么

让我们尝试创建一个具有默认非 root 用户的简单 Docker 应用程序。使用下面的Dockerfile

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

# create a dummy py file
RUN echo "print('I can run an existing py file')" > example.py

# create & switch to non-root user
RUN adduser --no-create-home nonroot
USER nonroot

构建镜像并使用它创建一个容器。

docker build -t test .
docker run -it test bash

现在你在容器内,让我们尝试几个命令。那么你不能做哪些事情呢?你可以看到各种写入和安装权限都是不允许的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作为默认的非 root 用户你不能做的事情。作者截图。

在相反的情况下,我们可以运行各种读取权限。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

读取命令是可以的。作者图片

由于我们安装了 python,它有些独特。如果我们ls -l $(which python),可以看到 python 解释器具有完全权限。因此,它可以执行像我们在Dockerfile中最初创建的example.py文件这样的现有 python 文件。我们甚至可以进入 python 控制台并运行简单命令。然而,当我们切换到非 root 用户时,其他系统写入权限已被移除,因此你会看到我们无法创建和修改脚本,或使用 python 执行写入命令。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以执行现有的 python 脚本,但其他操作则不可行。作者图片

虽然系统范围的限制对安全性有好处,但在许多情况下,特定文件和目录的写入权限是必需的,我们需要考虑这些许可。

在接下来的章节中,我将给出机器学习操作生命周期中的四种场景的示例。通过这些示例,可以了解如何实现大多数其他情况。

四种场景

1) 从主机服务模型 — 只读

服务模型时,它涉及到一个推理和服务脚本,用于加载模型并通过 API(例如 Flask、FastAPI)暴露它以接受输入。模型有时从主机加载,并与镜像分开,以使镜像大小尽可能小,任何重新加载镜像都将尽可能快速而不重复下载模型。然后,模型通过一个bind-mount卷传递到容器中,以便加载和服务。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

服务模型只需要读取权限。作者图片

这可能是实现非 root 用户的最简单方法,因为只需要读取权限,而所有用户默认都被授予此权限。下面是一个如何完成的示例Dockerfile

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip3 install --no-cache-dir --upgrade pip~=23.2.1 \
    && pip3 install --no-cache-dir -r requirements.txt

COPY ./project/ /app

# add non-root user ---------------------
RUN adduser --no-create-home nonroot
# switch from root to non-root user -----
USER nonroot

CMD ["python", "inference.py"]

它有两个简单的命令,首先创建一个名为nonroot的新系统用户。其次,在最后一行CMD之前,从 root 切换到nonroot用户。这一点很重要,因为默认的非 root 用户没有任何写入和执行权限,因此不能安装、复制或操作在早期步骤中所需的文件。

现在我们知道如何在 Docker 中分配非 root 用户,接下来我们进入下一步。

2) 运行数据处理管道 — 在容器内写入

有时,我们只是想存储临时文件以执行一些任务,例如,一些数据预处理工作。这包括添加和删除文件。我们可以在容器内完成这些任务,因为文件不是持久的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

写入文件。作者图片

如果我们使用非 root 用户,我们需要写入权限。为此,我们需要使用命令chown(更改所有者)并将特定文件夹的所有权分配给nonroot用户。完成后,我们可以切换到nonroot用户。

# Dockerfile

# ....

# add non-root user & grant ownership to processing folder
RUN adduser --no-create-home nonroot && \
    mkdir processing && \
    chown nonroot processing

# switch from root to non-root user
USER nonroot

CMD ["python", "preprocess.py"]

3) 库自动写入文件 — 在容器内写入

之前的示例展示了如何写入我们自己创建的文件。然而,库自动创建文件和目录也是很常见的。你只有在尝试运行容器时才会知道它们已被创建,并且被拒绝写入权限。

我将给你展示两个这样的示例,一个来自supervisor,用于管理多个进程,另一个来自huggingface-hub,用于从 huggingface 下载模型。如果我们切换到非 root 用户,将会看到类似的权限错误。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Supervisor 阻止了创建日志文件。作者截图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Supervisor 阻止了将 PID 存储在文件中。作者截图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Huggingface Hub 阻止了下载模型文件。作者截图。

对于两个supervisor文件,我们可以先将它们创建为空文件,并赋予所有权。对于huggingface-hub的下载问题,错误日志中已提示我们可以通过TRANSFORMERS_CACHE变量更改下载目录,因此我们可以先设置目录变量,创建目录,然后分配所有权。

# Dockerfile

# ....

# add non-root user ................
# change huggingface dl dir
ENV TRANSFORMERS_CACHE=/app/model

RUN adduser --no-create-home nonroot && \
    # create supervisor files & huggingfacehub dir
    touch /app/supervisord.log /app/supervisord.pid && \
    mkdir $TRANSFORMERS_CACHE && \
    # grant supervisor & huggingfacehub write access
    chown nonroot /app/supervisord.log && \
    chown nonroot /app/supervisord.pid && \
    chown nonroot $TRANSFORMERS_CACHE
USER nonroot

CMD ["supervisord", "-c", "conf/supervisord.conf"]

当然,还会有其他示例可能与我这里展示的略有不同,但允许最少写入权限的概念将是相同的。

4) 保存训练好的模型 — 写入主机

假设我们使用容器训练模型,并希望将该模型写入主机,例如,以便被另一个任务用于基准测试或部署。在这种情况下,我们需要通过将容器目录链接到主机目录来写入模型文件,这也称为绑定挂载。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将模型文件写入主机。作者图片

首先,我们需要为nonroot创建一个组和用户,并为每个指定唯一的 ID,在此情况下,我们使用1001(1000 以上的任意数字都可以)。然后,创建一个模型目录来存储模型。

与情境 2 相比的不同之处在于,模型目录的写入不需要chown。为什么?

# Dockerfile

# ....
# add non-root group/user & create model folder
ENV UID=1001
RUN addgroup --gid $UID nonroot && \
    adduser --uid $UID --gid $UID --no-create-home nonroot && \
    mkdir model

# switch from root to non-root user
USER nonroot

CMD ["python", "train.py"]

这是因为绑定挂载目录的权限由主机目录决定。因此,我们需要在主机中再次创建相同的用户,确保用户 ID 相同。然后在主机中创建模型目录,并将nonroot用户授予所有者权限。

# in host terminal

# add the same user & group
addgroup --gid 1001
adduser --uid 1001 --gid 1001 --no-create-home nonroot
# create model dir to bind-mount & make nonroot an owner
mkdir /home/model
chown nonroot /home/model

绑定挂载通常在docker-compose.yml文件或docker run命令中指定,以启用更多灵活性。以下是前者的一个示例。

version: "3.5"

services:
    modeltraining:
        container_name: modeltraining
        build:
            dockerfile: Dockerfile
        volumes:
            - type: bind
              source: /home/model # host dir
              target: /app/model  # container dir

对于后者:

docker run -d --name modeltraining -v /home/model:/app/model <image_name>

运行其中之一,你会看到你的非根用户可以毫无问题地执行脚本。

摘要

我们已经看到如何分配非根用户并仍然使容器完成其所需任务。这主要在需要特定写入权限时相关。我们只需了解两个基本概念。

  • 在容器中进行写入权限的设置,可以使用Dockerfile中的chown

  • 对于绑定挂载的写入权限,请在主机中创建相同的非根用户,并在主机目录中使用chown

如果你需要进入 Docker 容器并以 root 用户身份运行一些测试,我们可以使用以下命令。

docker exec -it -u 0 <container_id/name> bash

参考文献

查看你使用 SAM 的分割效果

原文:towardsdatascience.com/see-what-you-sam-4eea9ad9a5de?source=collection_archive---------1-----------------------#2023-05-03

如何生成和可视化 Segment Anything Model 的预测结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Jacob Marks, Ph.D.

·

关注 发表在 Towards Data Science ·10 分钟阅读·2023 年 5 月 3 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Meta AI 的 Segment Anything Model (许可证) 对 Open Images V7 的样本进行分割 (许可证)。图片由作者提供。

在过去的几周里,Meta AI Research 的通用图像分割模型吸引了大量关注。这个模型,名为 Segment Anything Model (SAM) (Apache 许可证 2.0),是在一个包含 1100 万张图像和超过十亿个分割掩码的数据集上进行训练的。

SAM 功能强大。但像往常一样,在将模型投入生产之前,你需要了解模型在你的数据集上的表现。在计算机视觉的背景下,关键因素之一是可视化模型预测。

本博文旨在帮助你快速上手 SAM:我们将引导你如何使用 SAM 将分割掩码添加到你的数据集中,以及如何系统地可视化整个数据集中的这些分割掩码。通过可视化(和评估)这些预测,我们可以更好地了解 SAM 在我们数据集上的表现、其局限性以及将模型集成到我们管道中的潜在影响。

SAM 提供了多种生成分割掩码的途径:

  1. 自动:它自动工作,无需任何提示或提示

  2. 从边界框:给定一个边界框,SAM 对边界内的对象进行分割

  3. 从点:给定点标签,可能是正数或负数,SAM 推断需要分割的区域。

  4. 从点和框:你可以提供点边界框来提高性能

接下来,我们将明确介绍前三个。本文将按以下结构组织:

  • 设置

  • 使用 SAM 进行自动分割

  • 使用 SAM 进行语义分割

  • 使用 SAM 进行实例分割

设置

安装

本教程要求python≥3.8pytorch≥1.7torchvision≥0.8。如果你没有安装 Torch 或 Torchvision,请运行:

pip install torch torchvision

此外,我们将使用开源计算机视觉库FiftyOne来加载数据集和可视化预测。如果你没有安装 FiftyOne,可以运行:

pip install fiftyone

为了使用 SAM,你可以从源代码安装Segment Anything 库,使用:

pip install git+https://github.com/facebookresearch/segment-anything.git

然后,你将能够将库导入为segment_anything

然后,下载一个模型检查点。在这次演练中,我们将使用默认的ViT-H SAM 模型,即“huge”视觉变换器分割模型。如果你愿意,你也可以使用大型(ViT-L SAM)或基础(ViT-B SAM)模型。

导入模块

这是我们需要导入所有模块的头部代码:

import numpy as np
import PIL
import torch

import fiftyone as fo
import fiftyone.zoo as foz # for loading/downloading datasets

from segment_anything import sam_model_registry, SamAutomaticMaskGenerator, SamPredictor

定义常量

我们还可以定义一些在所有分割应用中不会改变的元素:

sam_checkpoint = "path/to/ckpt.pth"
model_type = "vit_h"
device = "cuda"

sam = sam_model_registrymodel_type
sam.to(device)

加载数据集

对于本教程,我们将使用来自 Google 的Open Images V7Apache 许可证 2.0)数据集的图像。数据集已经为许多图像准备了实例分割掩码,但为了说明,我们只会加载点标签和目标检测边界框。关于如何处理 Open Images V7 中的点标签的全面教程,请查看此 Medium 文章

让我们从验证集中加载 100 个随机图像:

dataset = foz.load_zoo_dataset(
    "open-images-v7", 
    split="validation", 
    max_samples=100,
    label_types=["detections", "points"],
    shuffle=True,
)

我们将命名数据集并使其持久化。此外,我们还将通过运行compute_metadata()将图像的宽度和高度存储为像素,以便我们可以使用这些信息在绝对坐标和相对坐标之间进行转换:

dataset.name = "openimages_sam"
dataset.persistent = True
dataset.compute_metadata()

## visualize the dataset
session = fo.launch_app(dataset)

在我们开始添加 SAM 预测之前,数据集的外观如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Open Images V7 中的图像在 FiftyOne App 中可视化。图像由作者提供。

使用 SAM 进行自动分割

如果您没有任何现有的关键点或边界框来指导 Segment Anything 模型,您可以使用“自动分割”功能为图像中的任何对象和物品生成分割掩码。这是通过SamAutomaticMaskGenerator类完成的。请注意,这不是全景分割,因为掩码没有标注。

您可以实例化一个SamAutomaticMaskGenerator对象,并设置交并比(IoU)阈值、返回的掩码的最小面积和其他参数:

mask_generator = SamAutomaticMaskGenerator(
    model=sam,
    points_per_side=32,
    pred_iou_thresh=0.9,
    stability_score_thresh=0.92,
    crop_n_layers=1,
    crop_n_points_downscale_factor=2,
    min_mask_region_area=400
)

有关可接受参数的完整列表,请参阅此 SAM 笔记本

给定一个样本(位于sample.filepath的图像),我们可以使用 Pillow 读取图像,并调用我们的SamAutomaticMaskGenerator对象的generate()方法生成掩码:

image = np.array(PIL.Image.open(sample.filepath))
masks = mask_generator.generate(image)

这些掩码包含 2D 的“分割”数组,但没有标签。如果我们还想要标签,我们可以使用类似语义分割工具的库。为简单起见,我们将向您展示如何将所有这些组合成一个完整的图像掩码,并为我们的掩码生成器返回的每个单独的掩码分配一个不同的颜色。

要为单个样本添加“自动”分割掩码,我们可以将与该样本关联的图像传递给我们的掩码生成器。然后对于返回的每个掩码,我们可以将该掩码添加到完整的图像掩码中,乘以一个唯一的数字,使得显示颜色对应于该子掩码。然后,我们可以将这个完整的图像掩码作为Segmentation标签对象存储在我们的数据集中。

这包含在以下函数中:

def add_SAM_auto_segmentation(sample):
    image  = np.array(PIL.Image.open(sample.filepath))
    masks = mask_generator.generate(image)

    full_mask = np.zeros_like(masks[0]["segmentation"]).astype(int)
    for i in range(len(masks)):
        x, y = np.where(masks[i]['segmentation'])
        full_mask[x,y] = i + 1

    sample["auto_SAM"] = fo.Segmentation(mask=full_mask.astype(np.uint8))

只要在定义掩码生成器时设置crop_n_layers=1,添加步骤就是有效的。此代码可以处理最多 256 个唯一子掩码。

我们将遍历数据集中的样本,并在此过程中保存每个样本:

def add_SAM_auto_segmentations(dataset):
    for sample in dataset.iter_samples(autosave=True, progress=True):
        add_SAM_auto_segmentation(sample)

当我们在 FiftyOne App 中可视化结果时,我们看到的是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Meta AI 的 Segment Anything Model 对 Open Images V7 样本的自动分割。图片由作者提供。

观察这些自动生成的掩码,我们可以看到有一些很小的斑点对我们并没有特别的意义。当我们定义掩码生成器时,我们将最小掩码区域面积设置为 400 像素。如果我们将此方法用作更大管道的一部分,我们可能需要考虑增加这一最小要求,或者根据图像中的像素数量为某些图像使用不同的最小值。

使用 SAM 进行语义分割

如果你的数据集中图像上有点标签(关键点),你可以使用这些点标签来提示 SAM 模型。这对于正负点标签都是适用的!本节将向你展示如何做到这一点。

在 FiftyOne 中,点标签表示为Keypoint对象。在 Open Images V7 中,图像上显示的每个单独点都存储在“points”字段中的自己的Keypoint对象中,因为它携带了额外的信息。

我们可以通过keypoints属性访问给定样本的点标签内容。例如,要获取数据集中第一个样本的第一个点标签,我们可以使用:

dataset.first().points.keypoints[0]
<Keypoint: {
    'id': '644c260d753fe20b7f60f9de',
    'attributes': {},
    'tags': [],
    'label': 'Rope',
    'points': [[0.11230469, 0.7114094]],
    'confidence': None,
    'index': None,
    'estimated_yes_no': 'no',
    'source': 'ih',
    'yes_votes': 0,
    'no_votes': 3,
    'unsure_votes': 0,
}>

这个点是类别Rope的负标签(estimated_yes_no字段),其结果由单个yesno投票数决定。在 Open Images V7 数据集中,点标签有estimated_yes_no("yes", "no", "unsure")。我们将忽略unsure点(这仅占总点标签的一小部分),并关注高确定性的点。

让我们实例化一个 SAM 预测器模型,用于语义和实例分割:

predictor = SamPredictor(sam)

为了初始化预测器,我们将通过point_coordspoint_labels参数传递图像中的点标签信息。

SamPredictor期望point_coords使用绝对坐标,而 FiftyOne 存储相对坐标。此外,point_labels接受01的数组,所以我们将从[yes, no]转换过来。以下函数接受给定图像的点标签列表、标签类别、图像宽度和高度,并返回所有相关点的point_coordspoint_labels

def generate_sam_points(keypoints, label, w, h):
    def scale_keypoint(p):
        return [p[0] * w, p[1] * h]

    sam_points, sam_labels = [], []
    for kp in keypoints:
        if kp.label == label and kp.estimated_yes_no != "unsure":
            sam_points.append(scale_keypoint(kp.points[0]))
            sam_labels.append(bool(kp.estimated_yes_no == "yes"))

    return np.array(sam_points), np.array(sam_labels)

对于单个样本,我们可以使用以下函数添加 SAM 语义分割掩码:

def add_SAM_semantic_segmentation(sample, n2i):
    image = np.array(PIL.Image.open(sample.filepath))
    predictor.set_image(image)

    if sample.points is None:
        return

    points = sample.points.keypoints
    labels = list(set([point.label for point in points]))

    w, h = sample.metadata.width, sample.metadata.height
    semantic_mask = np.zeros((h, w))
    for label in labels:
        sam_points, sam_labels = generate_sam_points(points, label, w, h)
        if not np.any(sam_labels):
            continue

        masks, scores, _ = predictor.predict(
            point_coords=sam_points,
            point_labels=sam_labels,
            multimask_output=True,
        )
        mask = masks[np.argmax(scores)].astype(int) ## get best guess

        semantic_mask *= (1 - mask)
        semantic_mask += mask * n2i[label]

    sample["semantic_SAM"] = fo.Segmentation(
        mask=semantic_mask.astype(np.uint8)
    )

这里,n2i 是一个字典,将类名映射到整数值,用于填充分割掩码。值得注意的是,当multimask_output=True时,预测器会为每个输入返回多个分割掩码的猜测。我们选择置信度最高的预测(最大 score)。

遍历数据集中的样本:

def add_SAM_semantic_segmentations(dataset):
    point_classes = dataset.distinct("points.keypoints.label")
    dataset.default_mask_targets = {i+1:n for i, n in enumerate(point_classes)}
    dataset.default_mask_targets[0] = "other"  # reserve 0 for background
    NAME_TO_INT = {n:i+1 for i, n in enumerate(point_classes)}
    dataset.save()

    for sample in dataset.iter_samples(autosave=True, progress=True):
        add_SAM_semantic_segmentation(sample, NAME_TO_INT)

我们可以为数据集生成分割掩码:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Meta AI 的 Segment Anything 模型对 Open Images V7 样本的语义分割。图片由作者提供。

当然,并不是所有的内容都进行了语义分割,因为图像中包含一些稀疏的点标签。向初始数据中添加更多点将导致数据集中图像的语义分割掩码更加密集。

我们还可以看到,虽然 SAM 在整个数据集上的表现相当不错,但在适当地分割摩托车轮子方面表现得比较困难。

使用 SAM 进行实例分割

如果你已经有了数据集中对象的边界框,你可以用这些边界框来提示 SAM 模型,并生成这些对象的分割掩码!方法如下:

与点标签一样,我们需要将边界框从相对坐标转换为绝对坐标。在 FiftyOne 中,边界框的存储格式为 [<top-left-x>, <top-left-y>, <width>, <height>],坐标在 [0,1] 范围内。而 SAM 边界框的格式为 [<top-left-x>, <top-left-y>, <top-right-x>, <top-right-y>],使用绝对坐标。以下函数将为我们执行转换:

def fo_to_sam(box, img_width, img_height):
    new_box = np.copy(np.array(box))
    new_box[0] *= img_width
    new_box[2] *= img_width
    new_box[1] *= img_height
    new_box[3] *= img_height
    new_box[2] += new_box[0]
    new_box[3] += new_box[1]
    return np.round(new_box).astype(int)

一旦我们为给定的对象检测生成了实例分割掩码,我们可以使用以下方式将掩码添加到检测对象中:

def add_SAM_mask_to_detection(detection, mask, img_width, img_height):
    y0, x0, y1, x1 = fo_to_sam(detection.bounding_box, img_width, img_height)    
    mask_trimmed = mask[x0:x1+1, y0:y1+1]
    detection["mask"] = np.array(mask_trimmed)
    return detection

要将实例分割掩码添加到图像中,我们需要遍历所有对象检测,使用 SamPredictor 对象与每个检测的边界框,并将生成的掩码添加到 FiftyOne Detection 对象中:

def add_SAM_instance_segmentation(sample):
    w, h = sample.metadata.width, sample.metadata.height
    image = np.array(PIL.Image.open(sample.filepath))
    predictor.set_image(image)

    if sample.detections is None:
        return

    dets = sample.detections.detections
    boxes = [d.bounding_box for d in dets]
    sam_boxes = np.array([fo_to_sam(box, w, h) for box in boxes])

    input_boxes = torch.tensor(sam_boxes, device=predictor.device)
    transformed_boxes = predictor.transform.apply_boxes_torch(input_boxes, image.shape[:2])

    masks, _, _ = predictor.predict_torch(
            point_coords=None,
            point_labels=None,
            boxes=transformed_boxes,
            multimask_output=False,
        )

    new_dets = []
    for i, det in enumerate(dets):
        mask = masks[i, 0]
        new_dets.append(add_SAM_mask_to_detection(det, mask, w, h))

    sample.detections = fo.Detections(detections = new_dets)

对于实例分割,将其扩展到整个数据集是很简单的:

def add_SAM_instance_segmentations(dataset):
    for sample in dataset.iter_samples(autosave=True, progress=True):
        add_SAM_instance_segmentation(sample)

按标签着色,我们得到的效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Meta AI 的 Segment Anything 模型对 Open Images V7 样本的实例分割。图片由作者提供。

注意:为了提高效率,你还可以批量处理这些预测!

全景分割

如果你希望使用 SAM 对数据集进行全景分割,你可以通过以下方式结合关键点和边界框的方法:

对于每一个边界对象,或者事物,如汽车或桌子:

  1. 生成对象周围的边界框,可以通过传统注释,或使用Grounding DINO,或者其他方法。

  2. 选择边界框的中心点作为该对象的默认关键点。如果发现这个点不在对象内部,请相应调整。

  3. 使用这些关键点和边界框来计算实例分割掩码

对于每个连续的物体区域(例如天空或草地):

  1. 添加一个或多个标记的关键点。

  2. 使用这些关键点计算语义分割掩码

填补空白:

  1. 在所有实例和语义分割掩码的基础上,识别重叠区域和没有任何掩码的区域。

  2. 使用适合你应用的策略处理这些区域。

结论

Meta AI 的 Segment Anything Model 功能强大且多才多艺。尽管如此,SAM 只是分割和提示/引导计算机视觉领域众多令人兴奋的进展中的一个。这个领域发展非常迅速!如果你对了解更多感兴趣,我建议你查看以下相关项目:

来源

通过声音看见世界:利用 GPT-4V(ision)和文本转语音技术赋能视觉障碍者

原文:towardsdatascience.com/seeing-with-sound-empowering-the-visually-impaired-with-gpt-4v-ision-and-text-to-speech-bb5807b4e08c

提升视觉障碍导航:将 GPT-4V(ision)和 TTS 集成以提供高级感官辅助

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Luís Roque

·发表于Towards Data Science ·阅读时间 12 分钟·2023 年 11 月 16 日

本文由 Rafael Guedes 共同撰写。

介绍

OpenAI 的最新发展通过发布 GPT-4V(ision)和文本转语音(TTS) API,将 AI 的可用性提升到了一个全新的水平。为什么?让我们通过一个使用案例来激发它们的实际效用。对大多数人来说,走在街上是一个简单的任务,但对视觉障碍者来说,每一步都可能是一个挑战。传统的辅助工具如导盲犬和手杖虽然有用,但 AI 技术的融合开启了一个改善盲人社区独立性和移动性的全新篇章。配备隐蔽摄像头的简单眼镜足以彻底改变视觉障碍者体验周围环境的方式。我们将解释如何利用 OpenAI 的最新发布来实现这一点。

另一个有趣的使用案例是改变我们在博物馆和其他类似场所的体验。设想一下,博物馆中常见的音频导览系统被别在衣服上的隐蔽摄像头所取代。假设你正在参观一家艺术博物馆。当你在博物馆中漫步时,这项技术可以为你提供有关每幅画作的信息,并且可以按照你选择的特定风格进行讲解。假设你有点疲倦,需要一些轻松有趣的内容,你可以提示它*‘给我一些关于这幅画的历史背景,但要有趣和引人入胜,甚至可以加些笑话’*。

增强现实(AR)呢?这种新技术能否改善甚至取代它?目前,AR 被视为我们可以叠加在对现实世界的视觉感知上的数字层。问题是,这可能很快变得嘈杂。这些新技术可能会在某些用例中取代 AR。在其他情况下,它可以使 AR 个性化,使我们能够以自己的节奏体验世界。

在这篇文章中,我们将探讨如何将 GPT-4V(视觉)和文本转语音(TTS)结合起来,使世界对视觉障碍者更具包容性和可导航性。我们将首先解释 GPT-4V(视觉)是如何工作的及其架构(我们将使用一些开源对应物来获取直觉,因为 OpenAI 不提供有关其模型的详细信息)。接下来,我们将解释什么是 TTS,以及用于将文本转化为音频信号的最常见模型架构。最后,我们将通过一步一步的实施,展示如何利用 GPT-4V(视觉)和 TTS 帮助视觉障碍者在葡萄牙波尔图的街道上导航。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1:OpenAI 发布了有关其 API 服务的多个更新,并将多模态引入 GPT-4(来源

一如既往,代码可以在我们的 Github 上找到。

什么是 GPT-4V(视觉)?

GPT-4 像 GPT-3.5 一样,是一个大型多模态模型,能够处理文本输入并生成文本输出 [1]。在最新的 OpenAI 公告中,他们表示 GPT-4 已被扩展为一个多模态大型语言模型(LLM)。这意味着该模型现在能够接收额外的输入模态,在这种情况下是图像。多模态 LLM 扩展了仅语言系统的影响,通过新的接口和能力,为更复杂的用例开辟了可能性。你可以在下图中看到使用 GPT-4V(视觉)的示例,其中视觉和推理能力一起工作,以检测图片中的一些复杂细微之处。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2:GPT-4 解释对人类来说不寻常的能力(来源

尽管 OpenAI 在其论文中明确表示 …

考虑到竞争环境以及像 GPT-4 这样的大规模模型的安全性,这份报告未包含有关架构(包括模型大小)、硬件、训练计算、数据集构建、训练方法或类似内容的更多细节。

…我们可以尝试估计 GPT-4V(视觉)的架构是什么样的。

我们知道 GPT-4V(视觉)接收文本和图像作为输入。因此,它很可能有三个主要组件:

  1. 编码器: 用于处理文本和图像数据的独立编码器

  2. 注意力机制: 它采用先进的注意力机制,使模型能够关注文本和图像输入中最相关的部分。

  3. 解码器: 根据编码器的潜在空间结合注意力层生成输出文本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3:使用图像和文本作为输入的多模态模型的简单架构(图片由作者提供)

类似的架构可以在🦩 **Flamingo 模型 [2]**中找到,该模型由 DeepMind 创建。

Flamingo 旨在处理文本和视觉数据作为输入,以生成自由形式的文本作为输出。作者将其定义为视觉语言模型(VLM)。该模型有三个主要组件:输入处理、感知重采样器和整合两种数据类型并生成输出文本的层。

输入处理:Flamingo 接收视觉和文本数据。文本在进入语言模型(LM)之前经历常规的分词处理,而视觉输入由视觉编码器(VE)处理,将像素转换为更抽象的特征表示。

感知重采样器:此组件进一步处理视觉特征。它为图像添加时间感(在视频中尤为重要),并将数据压缩成更易于管理的格式。这对后续有效结合视觉和文本数据至关重要。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4:感知重采样器架构(来源

集成和输出: 处理过的视觉和文本数据随后被整合到 GATED XATTN-DENSE 层。该层采用了交叉注意力机制与门控函数结合,来有效地融合两种数据。该层的输出输入到 LM 层,最终由 Transformer 解码器生成自由形式的文本输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5:Flamingo 模型概述。Flamingo 模型是一系列视觉语言模型(VLM),可以同时接受视觉数据和文本作为输入,并生成自由形式的文本输出(来源)。

GPT-4V(ision) API

OpenAI 的 GPT-4V(ision) API 允许处理视觉和文本信息。我们在下面介绍使用该 API 的主要步骤。

设置环境

  • 在你的环境中安装 Python 依赖项,即 OpenAI 库。
pip install openai
  • 在你的 Python 脚本中导入必要的库。
import openai 
import os

配置 API 参数:利用**ChatCompletion**类,结合特定参数处理多模态(文本和图像)数据。

  • 模型参数:将其设置为**gpt-4-vision-preview**以启用对视觉和文本数据的处理。
params = {
    "model": "gpt-4-vision-preview",
    "messages": PROMPT_MESSAGES,
    "api_key": os.environ['OPENAI_API_KEY'],
    "headers": {"Openai-Version": "2020-11-07"},
    "max_tokens": 400,
}
  • 消息参数:这需要包括文本和图像。图像应以 base64 格式编码。
PROMPT_MESSAGES = [{
    "role": "user",
    "content": [
        "<Your Prompt>",
        {"image": image_in_base64_format}
    ],
}]

处理图像:在将其包含在 API 调用中之前,图像必须转换为 base64 格式。

import base64

def convert_image_to_base64(image_path):
    with open(image_path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode('utf-8')

执行 API 调用:设置好参数后,发起 API 调用以处理输入数据。

response = openai.ChatCompletion.create(**params)
print(response)

什么是文本到语音?

TTS [4] 技术将书面文字转换为口语。这一复杂过程涉及多个阶段,每个阶段由不同的模型处理。首先,一个 字形到音素 模型将书面文字转换为音素单位。接下来,一个 音素到 Mel 谱图 模型将这些音素转换为视觉频率表示。最后,一个 Mel-谱图到音频 模型或 声码器 从这种表示生成实际的口语音频。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6:TTS 架构(图片来源于作者)

  1. 字形到音素转换:第一步是将书写的单词(字形)转换为音素,即发音的基本单位。例如,短语 **"Swifts, flushed from chimneys"** 将被转换为音标表示,如 **"ˈswɪfts, ˈfɫəʃt ˈfɹəm ˈtʃɪmniz"**。一个常用的模型是 G2P-Conformer [5]。

  2. 音素到 Mel 谱图:接下来,这些音素会被处理以创建 mel-谱图,这是声音频率随时间变化的视觉表示。这通常通过编码器-解码器架构来实现,例如 Tacotron2 [6]。在这个模型中,卷积神经网络(CNN)嵌入音素,然后通过双向长短期记忆(LSTM)网络进行处理。生成的 mel-谱图是一个关键的中间步骤,充当音素与最终音频输出之间的桥梁。

  3. Mel-谱图到音频转换:最后阶段涉及将 mel-谱图转换为实际的音频。这时需要一个声码器,通常使用先进的生成模型。由 DeepMind 开发的 WaveNet [7] 是一个很好的例子。它使用带有扩张因果卷积的深度神经网络,以确保正确的序列处理。每个预测的音频样本会反馈到网络中以预测下一个,从而使模型能够捕捉音频信号中的长距离模式。

文本到语音 API

OpenAI 提供了一个通过 API 访问的 TTS 服务,提供两个质量等级和六种不同的声音,以满足不同的需求和偏好。

质量选项

  • **tts-1**:适用于实时应用,这个选项提供较低的质量但具有减少延迟的优点。

  • **tts-1-hd**:适合于音频质量较高且延迟不是问题的场景。

选择声音

  • OpenAI 的 TTS API 具有六种独特的声音:**alloy****echo****fable****onyx****nova****shimmer**

  • 每种声音都有其独特的特性;例如,**Fable** 类似于播客叙述者的声音。

  • 你可以在 OpenAI 的 文本到语音指南 上预览这些声音。

发起 API 请求:

要使用 OpenAI 的 TTS API,发送请求到 **https://api.openai.com/v1/audio/speech**。你需要以下内容:

  1. 模型规格:根据你的需求选择 **tts-1**(低质量,低延迟)或 **tts-1-hd**(高质量,高延迟)。

  2. 输入文本:你想要转换为语音的文本内容。

  3. 语音选择:从可用的语音中选择一个最适合你的内容和观众的声音。

这是一个关于如何结构化 API 请求的基本示例:

response = requests.post(
    "https://api.openai.com/v1/audio/speech",
    headers={
        "Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}",
    },
    json={
        "model": "tts-1",
        "input": result.choices[0].message.content,
        "voice": "fable",
    },
)

如何实现一个帮助视障人士在街上行走时感到更安全的应用?

本节逐步描述了如何使用 OpenAI 的 GPT-4V(ision) 和 TTS 创建视频的音频描述。还涵盖了如何将生成的音频添加到视频中。

在我们的案例中,如介绍所述,我们创建了一个音频指南,帮助视障人士在街上行走,通过识别障碍物的位置并提供空间信息来辅助他们。

过程开始于导入必要的库并设置 Python 环境。我们使用如 cv2 进行视频处理和 openai 访问 GPT-4V(ision) 和 TTS 模型等库。

import base64
import cv2
import openai
import os
import requests
import time

from IPython.display import display, Image, Audio
from moviepy.editor import VideoFileClip, AudioFileClip
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip

接下来,我们加载并处理视频。视频被调整为可管理的分辨率,以确保不超过 OpenAI 的令牌限制。每一帧都被编码为 base64,这是 OpenAI API 所需的格式。

video = cv2.VideoCapture("video.mp4")

base64Frames = []
while video.isOpened():
    success, frame = video.read()
    if not success:
        break
    frame = cv2.resize(frame, (512,512))
    _, buffer = cv2.imencode(".jpg", frame)
    base64Frames.append(base64.b64encode(buffer).decode("utf-8"))

video.release()
print(len(base64Frames), "frames read.")

一个重要的步骤是为 GPT-4V(ision) 制作合适的提示。精心设计的提示会显著影响我们从模型中获得的输出。从我们的实验中,影响输出的两个主要组件是:

  • 动词如描述、叙述、讲述;

  • 确定我们想要的语音风格。

我们尝试的第一个提示之一是:‘这些是一个人在城市中行走的画面。描述周围的元素和障碍物,以帮助盲人成功导航。’ 这种结构使模型变得极其冗长和描述性。我们的使用案例需要输出更少噪音。

对我们有效的提示是:‘这些是一个人行走的画面。用 GPS 设备那样的简短句子简洁叙述周围的元素和障碍物,以帮助盲人成功导航。’ 这次模型给出了简短的句子,让我们获得了街道导航所需的基本信息。结果如下:

“在有纹理的路径上直行,将建筑物保持在右侧。继续前行,稍微向右转。保持直行,前方右侧有小悬挑。继续前进,经过悬挑,继续在平坦的路径上前行。直行,接近一个光线充足的区域。经过光线充足的区域后,过渡到有图案的人行道。跟随有引导线的瓷砖人行道直行。继续通过通道,保持柱子与自己平行。穿过通道,前方有小的下降。路径结束,准备在斑马线停下。站在斑马线前,等待听到过马路的信号。”

PROMPT_MESSAGES = [
    {
        "role": "user",
        "content": [
            "These are frames from a person walking. Narrate succinctly with short sentences like they do in GPS devices the elements and obstacles around you to help a blind person go through.",
            *map(lambda x: {"image": x}, base64Frames[0::100]),
        ],
    },
]

params = {
    "model": "gpt-4-vision-preview",
    "messages": PROMPT_MESSAGES,
    "api_key": os.environ['OPENAI_API_KEY'],
    "headers": {"Openai-Version": "2020-11-07"},
    "max_tokens": 350,
}

result = openai.ChatCompletion.create(**params)
print(result.choices[0].message.content)

一旦我们收到 GPT-4V(ision)的描述,下一步是将文本转换为音频。我们选择了寓言风格的声音,因为它的清晰度和叙述的相似性。使用 OpenAI 的 TTS API 将生成的文本转化为音频文件。

response = requests.post(
    "https://api.openai.com/v1/audio/speech",
    headers={
        "Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}",
    },
    json={
        "model": "tts-1",
        "input": result.choices[0].message.content,
        "voice": "fable",
    },
)

audio = b""
for chunk in response.iter_content(chunk_size=1024 * 1024):
    audio += chunk

with open('audio.mp3', 'wb') as file:
    file.write(audio)

最后一步是将音频与原始视频合并。

# Open the video and audio
video_clip = VideoFileClip("video.mp4")
audio_clip = AudioFileClip("audio.mp3")

# Concatenate the video clip with the audio clip
final_clip = video_clip.set_audio(audio_clip)
final_clip.write_videofile("video_audio.mp4")

你可以在这里查看最终视频。

结论

OpenAI 的 GPT-4V(ision)和 TTS API 开辟了新的可能性,解决了可以改变许多人日常生活的重要用例。我们探讨了一个专注于视障人士的包容性的用例,但我们本可以选择其他许多用例。如在介绍中所述,它们还让我们能够提升人类体验(例如博物馆导览),并根据个人的偏好和情况进行更好的定制。

在实施过程中,我们发现提示工程对将解决方案量身定制到我们的特定用例有显著影响。未来,其他方法如微调或某种类型的检索增强生成(RAG)可能会适用于 VLMs。我们看到这些方法在某些任务和情境中使 LLMs 更为有效。尽管输出展示了这些新模型的潜力,但仍有待完善。如我们实验中所见,VLM “说话”的方式像是你可以看到它所说的“跟随有引导线的瓷砖人行道直行。” 它还难以准确区分左右,这是一个值得进一步探索的有趣事实。

尽管面临这些挑战,OpenAI 的最新进展向我们展示了一个更加包容的未来,AI 可以提升体验。

保持联系:LinkedInX/TwitterMedium

参考文献

[1] OpenAI. (2023). GPT-4 技术报告。arXiv 预印本 arXiv:2303.08774。

[2] Alayrac, J.-B., Donahue, J., Luc, P., Miech, A., Barr, I., Hasson, Y., Lenc, K., Mensch, A., Millican, K., Reynolds, M., Ring, R., Rutherford, E., Cabi, S., Han, T., Gong, Z., Samangooei, S., Monteiro, M., Menick, J., Borgeaud, S., Brock, A., Nematzadeh, A., Sharifzadeh, S., Binkowski, M., Barreira, R., Vinyals, O., Zisserman, A., & Simonyan, K. (2022). Flamingo: 一种用于少样本学习的视觉语言模型。arXiv 预印本 arXiv:2204.14198。

[3] Brock, A., De, S., Smith, S. L., & Simonyan, K. (2021). 无需归一化的高性能大规模图像识别。arXiv 预印本 arXiv:2102.06171。

[4] Maheshwari, H. (2021 年 5 月 11 日). 基础文本到语音,解析。Towards Data Science。取自 towardsdatascience.com/text-to-speech-explained-from-basic-498119aa38b5

[5] Gulati, A., Qin, J., Chiu, C.-C., Parmar, N., Zhang, Y., Yu, J., Han, W., Wang, S., Zhang, Z., Wu, Y., 等. (2020). Conformer: 用于语音识别的卷积增强变换器。arXiv 预印本 arXiv:2005.08100。

[6] Shen, J., Pang, R., Weiss, R. J., Schuster, M., Jaitly, N., Yang, Z., Chen, Z., Zhang, Y., Wang, Y., Skerry-Ryan, R. J., Saurous, R. A., Agiomyrgiannakis, Y., & Wu, Y. (2018). 通过将 WaveNet 条件化于 Mel 频谱预测进行自然 TTS 合成。arXiv 预印本 arXiv:1712.05884。

[7] van den Oord, A., Dieleman, S., Zen, H., Simonyan, K., Vinyals, O., Graves, A., Kalchbrenner, N., Senior, A., & Kavukcuoglu, K. (2016). WaveNet: 一种原始音频的生成模型。arXiv 预印本 arXiv:1609.03499。

Segment Anything 3D for Point Clouds: 完整指南 (SAM 3D)

原文:towardsdatascience.com/segment-anything-3d-for-point-clouds-complete-guide-sam-3d-80c06be99a18

3D Python

如何利用 SAM 和 Python 构建 3D 点云的语义分割应用程序。附加内容:投影和 3D 点与 2D 像素之间关系的代码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Florent Poux, Ph.D.

·发表于 Towards Data Science ·阅读时间 27 分钟·2023 年 12 月 13 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

针对 3D 环境的 Segment Anything 模型。我们将使用 3D 点云数据集检测室内空间的物体。特别鸣谢 Mimatelier,这位才华横溢的插画师创作了这张图片。

科技的飞跃真是疯狂,特别是看到人工智能(AI)应用于 3D 挑战时。能够利用最新的前沿研究来进行高级 3D 应用是非常令人振奋的。尤其是在将人类级别的推理能力带入计算机时,明确从我们观察到的 3D 实体中提取出正式化的意义显得尤为重要。

在本教程中,我们旨在将令人惊叹的人工智能进展与利用 3D 点云的 3D 应用程序结合起来。— 🐲 Florent & Ville

这不是一件容易的事,但一旦掌握,3D 点云与深度学习的融合将开辟对我们视觉世界的新维度的理解和解释。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

人工智能与 3D 点云。© F. Poux

在这些进展中,Segment Anything 模型是最近的创新灯塔,尤其是在无需监督的全自动化方面。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们用于 3D 数据的 Segment Anything 模型架构。它包括一个图像编码器、图像嵌入以及一些预处理操作,最后传递给解码器和提示编码器,输出结果为掩模。© F. Poux

在这份终极指南中,我们将开始一个实际的旅程,从模型的诞生到实际分割应用。但这里的目标是什么?

任务 🥷

好了,任务简报时间到了!你现在是你国家特种部队的多类成员,你必须在不被发现的情况下找到隐藏在特定建筑物中的危险材料(这里是 ITC 大楼)。

利用你出色的互联网黑客技能,你成功找到了你感兴趣的建筑部分的 3D 扫描。你现在需要快速找到定义你危险材料回收队的路径的方法。之后,队伍可以在不被察觉的情况下进行材料回收,你也就成功拯救了世界!

经过仔细研究并利用你的各种技能,你开发了一个 3D 数据处理工作流程,其中包括设置 3D Python 代码环境,通过 Segment Anything 模型处理 3D 点云,以突出场景的组成,如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Segment Anything 3D 的工作流程。我们有五个主要步骤(3D 项目设置、Segment Anything 模型、3D 点云投影、无监督分割和定性分析),并在下图中进一步细化。© F. Poux

这将允许你生成一个 3D 语义地图,在队伍到达现场之前的九十分钟内,能够准确定位材料的位置。你准备好了吗?

🎵读者注意: 这个实践指南是* UTWENTE 与合著者F. Poux* V. Lehtola* 的联合工作的一部分。我们感谢来自数字双胞胎 @ITC 项目的资助,该项目由特温特大学 ITC 学院授予。

1. 3D 项目设置

在我们深入探讨 Segment Anything 模型的奇迹之前,建立一个稳固的基础至关重要。设置适当的环境可以确保我们在整个过程中顺利进行,从而实现无缝的实验和探索。

在这个阶段,我们要确保我们的编码环境设置正确,并且具有强大的库。准备好了吗?

🤠 Ville: 这是在行动开始之前。如果你是从零开始做这件事,比如可能需要更新 CUDA 驱动程序,请单独预留一个或两个小时。你将下载几 GB 的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3D 项目设置。我们首先设置环境,然后附加基础库、深度学习库和 IDE 设置。© F. Poux

1.1. 3D 代码环境设置

现在是时候动手了!我们的目标是使用 Segment Anything Model 对 3D 点云进行语义分割。这绝非易事!所以,首先的反应是查看 Segment Anything 的依赖项:访问 SAM Github

从那里,我们检查包的必要前提条件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Segment Anything 中突出的依赖项。

🦊 Florent无论何时处理深度学习库或深度学习的新研究代码,都必须检查依赖项和安装建议。这将极大地影响实验的后续进展和复制所需的时间。

如你所见,我们需要使用以下库版本:

python ≥ 3.8
pytorch ≥ 1.7
torchvision ≥ 0.8

现在这一点搞定后,我们将生成一个虚拟环境以确保顺利进行!如果你想详细了解这一过程,建议你查看以下指南:

## 3D Python Workflows for LiDAR City Models: A Step-by-Step Guide

解锁 3D 城市建模应用程序的精简工作流的终极指南。教程涵盖了 Python…

towardsdatascience.com

为了不让你感到无助,下面是另一种快速轻量级的设置策略,使用 Miniconda

💡 注意Miniconda 是一个免费的 Conda 最小安装程序。它是 Anaconda 的“微型”版本,仅包含最少的依赖项。这些包括 Conda 包管理器、一个 Python 版本、它们所依赖的包以及其他有价值的包如 pip 和 zlib。这使得我们可以以轻量的方式只安装我们需要的东西。

🤠Ville*:虚拟环境的酷炫之处在于,你可以将其导出并在强大的 Linux 计算机和超级集群上直接运行你的代码!这对于训练网络非常有用!*

在从 这里 下载适合你操作系统的 Miniconda 版本后(建议选择 Python 3.9 或 3.10 版本以确保与包的兼容性),你可以按照安装过程中的各种步骤进行安装。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

miniconda 安装程序窗口。© F. Poux

就这样!你现在已经完成了最简单的 Python 安装,使用轻量级的 miniconda 使得隔离受控的虚拟环境变得非常容易。在继续下一步之前,我们启动 miniconda 及其命令行访问:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Windows 中,只需搜索“miniconda”即可找到

一旦进入 Anaconda 提示符,我们按照下面显示的简单四步过程进行操作。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

设置 Python 环境以使用 3D Segment Anything Model 的工作流程。© F. Poux

  1. 要创建新环境,我们输入:conda create -n GEOSAM python=3.10

  2. 切换到新创建的环境,我们输入:conda activate GEOSAM

  3. 要检查 Python 版本,输入 python --version,检查已安装的软件包:conda list。这应分别显示 Python 3.10 和基础库列表。

  4. 在新环境中安装 pip,我们输入:conda install pip

就这些!我们现在准备安装必要的库以进行 SAM 的操作。

[## 3D 创新者通讯

每周提供实用内容、见解、代码和资源,掌握 3D 数据科学。我写关于点云、人工智能……

learngeodata.eu

1.2. 基础库

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本教程中使用的基础库(Numpy、Matplotlib、Laspy)。© F. Poux

我们现在安装用于 SAM 的基础库:NumPyLasPyOpenCVMatplotlibNumPy 可能是最推荐的数值计算库,OpenCV 用于计算机视觉任务,Laspy 处理 LIDAR 数据,而 Matplotlib 是一个绘图和数据可视化库。

🦊 Florent这些库是任何 3D 项目的基础和坚实的基石。如果你想深入了解它们,我建议你去 这个教程 ,它探讨了其深奥的内容 🪸。

要安装这些库,我们可以用一行代码通过 pip:

pip install numpy matplotlib laspy opencv-python

很好;是时候设置深度学习库了!

1.2 深度学习库

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

深度学习库。

我们现在将着手安装深度学习库。当然,我们首先探索的是我迄今为止最喜欢的:Pytorch。自 2017 年推出以来,Pytorch 优先考虑其灵活性和可黑客性,其次是性能。因此,今天,使用 Pytorch 进行深度学习应用是绝佳的选择,如果你需要 (1) 高性能执行,(2) Pythonic 内部实现,以及 (3) 有价值任务的良好抽象。

🦊 Florent: 自 2017 年以来,硬件加速器(如 GPU)在计算任务中的速度提高了约 15 倍。你只能猜测接下来几年会发生什么。因此,必须关注灵活的库,它们可以快速适应,甚至对“内部”进行重构,如 Pytorch 所做的那样。

🤠Ville*: SAM 作者推荐使用 8GB 内存的 GPU。然而,我们提供了一些如何在内存较少的情况下进行教程的技巧。如果你收到‘MemoryError’或‘Out-of-bounds memory access’或‘Illegal memory access’消息,请使用这些技巧。我使用 6GB 内存成功运行了它。*

为了无忧地安装 Pytorch 的相关发行版,而不必为如何安装 CUDA(这并不简单)而烦恼,他们制作了一个简单的网页应用程序,生成代码以复制并粘贴到你的命令行中。为此,你可以访问这个 Pytorch 入门页面 并选择最相关的安装方式,如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如何根据你的操作系统和配置安装 Pytorch。

💡 注意: 我们希望充分利用我们的 GPU。因此,重要的是要注意我们希望进行 CUDA 安装。但这只有在你写这篇文章时拥有 Nvidia GPU 时才可能。如果没有,你可能需要使用 CPU 或切换到像 Google Colab 这样的云计算服务。

因此,我们的代码行如下:

conda install pytorch torchvision torchaudio pytorch-cuda=11.7 -c pytorch -c nvidia

这行代码将触发必要元素的检索和安装,以便 Pytorch 可以顺利运行。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Pytorch 的安装。

我们要使用的第二个深度学习库是 Segment Anything。在 Pytorch 安装的同时,我们可以下载并安装“软件”,这将使我们更容易管理版本和访问在线库。这就是Git,可以在这里访问:Git 官网

你可以下载并安装 git,一旦安装完成,Pytorch 也应该在你的环境中顺利安装。因此,为了安装 segment-anything,我们可以写如下代码:

pip install git+https://github.com/facebookresearch/segment-anything.git

这将再次需要一些时间,直到你看到如下消息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

安装 Pytorch 的 CLI 结果。

在这一阶段,我们已经安装了基础库以及深度学习库。在使用它们之前,让我们安装一个 IDE,以便一切顺利运行。

4. 设置 IDE

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Jupyter lab IDE 安装后的界面。

我们设置的最后一步是安装一个 IDE。我们仍在环境中的命令行界面下,输入:pip install jupyterlab,这将会在我们的环境中安装 jupyterlab。

要在指定的本地文件夹中使用它,我们可以首先为我们的项目创建一个父目录(我们称之为 SAM),该目录将包含一个 CODE 文件夹和一个 DATA 文件夹。完成后,在控制台中,我们通过写入 cd C://COURSES/POUX/SAM 来切换到创建的目录。

我们通过在控制台输入 jupyter lab 来从这个位置启动 jupyterlab,这将会在你的网页浏览器(Chrome、Firefox 或 Safari)中打开一个新的本地主机页面。

在 Jupyter 中,你可以创建一个笔记本 (.ipynb),并在第一个单元格中写入导入语句,以使用所有已安装的软件包:

# The Base libraries
import numpy as np
import matplotlib.pyplot as plt
import cv2
import laspy

# The Deep Learning libraries
import torch
from segment_anything import sam_model_registry
from segment_anything import SamAutomaticMaskGenerator

好的!我们已经准备好了一切。在开始编码模型的其他步骤之前,现在正是提取我们 3D 数据集的好时机。

5. 3D 数据集整理

在之前的教程中,我们展示了多个 3D 数据集中的点云处理和网格化,其中一些使用了 AHN4 LiDAR 活动的航空 LiDAR。

## 3D 深度学习 Python 教程:PointNet 数据准备

终极 Python 指南,用于结构化大型 LiDAR 点云,以训练 3D 深度学习语义分割……

towardsdatascience.com

这次,我们将使用一个使用地面激光扫描仪收集的数据集:2023 年乌特勒支大学 ITC 的新建筑,如下所示。它包含一个室内绿色区域,内有精美的复杂树叶,分割后可以进行评估。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ITC UTwente 新建筑的 3D 点云,包括其室内“丛林”。© F. Poux

你可以从这里下载数据: 指南数据集(Google Drive),并将其放入你保存数据集的文件夹中(在我的例子中是“DATA”)。

在这个过程中阶段,我们有一个良好的编码设置,所有必要的库都在一个轻量级、隔离的 GEOSAM conda 环境中。

🦊 Florent: 到目前为止做得很好!如果你急于运行一些测试以检查 Pytorch 是否正常工作,即 CUDA 是否被识别,你可以写下以下代码行:

import torch
print('CUDA available -> ', torch.cuda.is_available())
print('CUDA GPU number -> ', torch.cuda.device_count())
print('GPU -> ', torch.cuda.get_device_name())

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上述打印结果的配置。

现在是时候对数据进行分割了!

[## 3D 创新者通讯

每周实用内容、见解、代码和资源,以掌握 3D 数据科学。我写关于点云、人工智能等内容……

learngeodata.eu

2. 设置 Segment Anything 模型

我们的小冒险的核心是 Segment Anything Model,这是一种强大的创作,具有极好的 3D 点云语义分割潜力。凭借其创新的架构和训练过程,该模型是室内应用测试的理想候选者。让我们先来了解一下其核心概念。

2.1. Segment Anything 基础知识

MetaAI 已深入探讨自然语言处理(NLP)和计算机视觉的迷人领域,其 Segment Anything Model 使 零-shot少-shot 学习 在新数据集和任务上成为可能,使用基础模型。

🦊 Florent: 好吧,我承认有很多脏话。为了清晰起见,这里是我对每个复杂术语的总结尝试。零-shot 学习指的是在未见过某物的情况下识别它(零次见过)。类似地,少-shot 学习使用有限数量的标记示例来处理每个新类别,目标是根据这些少量的标记数据进行预测。

🤠Ville*: 此外,所谓的* 基础模型 是一个在大量数据上训练的模型。它如此庞大,可以适应来自不同场景的各种任务。

让我们为你拆解一下:

总体而言,SAM “AI” 算法可以显著减少进行图像分割所需的人力。为此,你需要向模型提供前景/背景点、粗略的框或掩码、一些文本或任何其他指示你想要在图像中分割的输入。Meta AI 团队已训练 Segment Anything Model 以生成合适的分割掩码。这个掩码是模型的输出,应该是一个适合划定提示可能指向的事物的掩码。例如,如果你在房子屋顶上标出一个点,输出应该正确识别你是指屋顶还是房子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Segment Anything Model (SAM) 是如何工作的?解释分割提示以生成有效的掩码(以房屋为例)。© F. Poux

该分割任务可以用于模型预训练,并指导解决各种下游分割问题。

从技术角度来看,我们所称的图像编码器为每张图像创建了一个独特的嵌入(表示),而一个轻量级的编码器迅速将任何查询转换为嵌入向量。这两个数据源通过一个(轻量级的)掩码解码器合并,以预测分割掩码,如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Segment Anything Model 的工作流程图。图像经过图像编码器处理。然后它被嵌入,最后在使用提示和提示编码器后合并,以生成我们 3D 点云的最终掩码。© F. Poux

这种有效的架构,加上大规模的训练阶段,使 Segment Anything Model 达到了四个里程碑:

  • 轻松对象分割 🔥: 使用 SAM,用户可以通过简单选择要包括或排除的点来轻松分割对象。你还可以使用边界框作为模型的提示。

  • 处理不确定性 🔥: SAM 能够处理对象分割中的不确定情况。它可以生成多个有效的掩码,这对于有效解决实际的分割挑战至关重要。

  • 自动对象检测与掩膜 🔥: SAM 使得自动对象检测和掩膜变得轻而易举。它简化了这些任务,节省了你的时间和精力。

  • 实时交互 🔥: 得益于预计算的图像嵌入,SAM 可以即时提供任何提示的分割掩膜。这意味着你可以与模型进行实时交互。

既然这些都解决了,你准备好使用它了吗?

2.1. SAM 参数

SAM 模型可以通过三种不同的编码器加载:ViT-B、ViT-L 和 ViT-H。ViT-H 的结果优于 ViT-B,但与 ViT-L 相比仅有微小的提升。

|     Encoder          |   #parameters    |     Speed      |   Quality    |
|----------------------|------------------|----------------|--------------|
|   ViT-B   (basic)    |       91M        |     Fastest    |   Low        |
|   ViT-L   (large)    |       308M       |     Fast       |   High       |
|   ViT-H   (huge)     |       636M       |     Slow       |   Highest    |

🤠 Ville: 为了帮助选择,我在 NVIDIA GeForce GTX 1650、6 Gb VRAM 和 Win11 上测试了 ViT-B。

这三种编码器具有不同的参数数量,这为应用程序的调优提供了更多自由。ViT-B(最小)有 9100 万个参数,ViT-L 有 3.08 亿个参数,而 ViT-H(最大)有 6.36 亿个参数。

这种大小差异也会影响推断速度,因此这应有助于你为你的具体用例选择编码器。按照本指南,我们将使用重型武器:ViT-H,带有一个可以从Github(2.4 Gb)下载的模型检查点,并将其放置在你的当前父文件夹中,例如。

在这里,我们可以定义两个变量,以使你的代码在之后稍微更灵活一些:

MODEL = "../../MODELS/sam_vit_h_4b8939.pth"

#You can run the line below to test if you have the ability to leverage CUDA
torch.cuda.is_available()

#Choose between cpu or cuda training. For cpu, input 'cpu' instead 'cuda:0'
USED_D = torch.device('cuda:0')

从这里,我们可以用以下两行代码初始化我们的 SAM 模型:

sam = sam_model_registry"vit_h"

#Cast your model to a specific device (cuda or cpu)
sam.to(device = USED_D)

一切都准备好了!也许最后一步,试试看它在你桌面上的随机图像上的表现如何?

2.2 在 2D 图像上的性能

让我们测试一下在随机图像上的效果是否如预期。我们对地理空间应用感兴趣,所以我去Google Earth并放大一个感兴趣的点:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

选择来自 Biscarosse 的图像数据集。© F. Poux

🦊 Florent: 这个点有偏见,对吧?希望这能给你一些法国假期的感觉,你很自豪地经历了这段美妙的岁月,充满了激动人心的项目!

从那里,我会截取一个感兴趣区域的屏幕截图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个来自 Biscarosse plage 区域的图像数据集。© F. Poux

然后我用 openCV 将图像加载到内存中:

#When loading an image with openCV, it is in bgr by default
loaded_img = cv2.imread("../DATA/biscarosse.jpg")

#Now we get the R,G,B image
image_rgb = cv2.cvtColor(loaded_img, cv2.COLOR_BGR2RGB)

🦚 注意: 如你所见,默认情况下,OpenCV 通过切换到蓝色、绿色和红色通道(BGR)来加载图像,我们通过第二行将其排序为 RGB,并存储在 image_rgb 变量中。

现在,我们可以用两行代码在图像上应用 SAM:

mask_generator = SamAutomaticMaskGenerator(sam)
result = mask_generator.generate(image_rgb)

大约 6 秒钟后,这将返回一个填充了字典的列表,每个字典代表一个特定对象的自动提取掩膜,并附有其分数和元数据。详细查看时,结果是一个字典列表,每个dict包含以下信息:

  • segmentation : 这会生成形状为(W, H)(和bool类型)的掩膜,其中W(宽度)和H(高度)针对原始图像尺寸;

  • area : 这是以像素为单位的掩膜面积

  • bbox : 这是xywh格式的边界框检测

  • predicted_iou : 模型对掩膜质量的预测 IoU 指标。

  • point_coords : 这是用于生成掩膜的采样输入点的列表

  • stability_score : 稳定性得分是掩膜质量的附加衡量指标。查看论文获取更多细节 😉

  • crop_box : 这是用于生成该掩膜的 crop_box 坐标列表,格式为xywh(可能与边界框不同)

现在你对我们正在处理的内容有了更好的了解,要查看结果,我们可以用以下函数在图像上绘制掩膜:

def sam_masks(anns):
    if len(anns) == 0:
        return
    sorted_anns = sorted(anns, key=(lambda x: x['area']), reverse=True)
    ax = plt.gca()
    ax.set_autoscale_on(False)
    c_mask=[]
    for ann in sorted_anns:
        m = ann['segmentation']
        img = np.ones((m.shape[0], m.shape[1], 3))
        color_mask = np.random.random((1, 3)).tolist()[0]
        for i in range(3):
            img[:,:,i] = color_mask[i]
        ax.imshow(np.dstack((img, m*0.8)))
        c_mask.append(img)
    return c_mask

🦊 Florent: 我承认,这有点直接。但在这个函数中,我会按掩膜面积对它们进行排序,以随机颜色和透明度参数在图像上绘制它们。

🤠Ville*: 内存错误可能会毁掉法国假期的氛围!记得使用 Google Colab 选项!如果重启不能解决问题且分配内存过高,以下代码可以清除 GPU 内存中的额外分配。用它来解决内存问题。*

print('Mem allocated by other programs: ', torch.cuda.memory_allocated(), 'reserved:', torch.cuda.memory_reserved())
import os
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "max_split_size_mb:512"
import gc
gc.collect()
torch.cuda.empty_cache()

If the GPU memory is not freed enough, try rebooting your (Windows) computer.
ALSO, try using the following line if memory problems persist
mask_generator = SamAutomaticMaskGenerator(sam, points_per_batch=16)

如果 GPU 内存未充分释放,请尝试重启你的(Windows)计算机。如果内存问题仍然存在,可以尝试使用以下行: mask_generator = SamAutomaticMaskGenerator(sam, points_per_batch=16)

现在,要绘制和导出图像,我们写下以下内容:

fig = plt.figure(figsize=(np.shape(image_rgb)[1]/72, np.shape(image_rgb)[0]/72))
fig.add_axes([0,0,1,1])
plt.imshow(image_rgb)
color_mask = sam_masks(result)
plt.axis('off')
plt.savefig("../test_result.jpg")

这导致:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Segment Anything Model 之前和之后。© F. Poux

因此,在这个阶段,我们已经有了令人兴奋的结果,SAM 工作得非常好!例如,你可以看到几乎所有的屋顶都是分段的一部分,三个泳池(两个蓝色和一个绿色)也是分段的一部分。因此,这可能是完全自动检测的起点

🦊 Florent: 根据你的计算机设置,绘制掩膜时可能会遇到内存错误。在这种情况下,加载一个更轻的 SAM 模型应该能解决你的问题。 😉

既然我们已经有了一个有效的 SAM 设置,让我们将这些辛苦获得的知识应用于 3D 点云。

3. 3D 点云到图像投影

为了理解复杂的 3D 世界,我们深入探讨点云投影的艺术。通过像正射和球面投影这样的技术,我们弥合了维度之间的差距,使我们能够在 2D 领域中可视化点云的复杂性,这正是 SAM 所需的输入。点云映射为这一投影过程增添了一层理解。

3.1 正射投影:平展维度,扩展洞察

让我们看看正射投影的变革性技术。此方法作为 3D 点云的多维复杂性与 2D 图像的可理解世界之间的绝佳桥梁。通过正射投影,我们“平展”维度,同时揭示了使用 SAM 进行分割的直接方法。

这个想法基本上是生成一个俯视视图平面,并生成一个不受单一视角限制的图像。你可以将正射投影视为将点云中的可见点(最高点)推送到持有空图像的平面上,以填充所有必要的像素。你可以看到与透视视图的不同之处,如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

解释正射视图和透视视图在 3D 投影中的区别。透视视图与一个单独的视点相关,这会扭曲维度。© F. Poux

为了完成这个过程,我们可以定义一个 3D 到 2D 的投影函数,它将点云的点及其颜色和所需分辨率作为输入,计算正射投影并从点云中返回正射图像。这将转化为以下内容:

def cloud_to_image(pcd_np, resolution):
    minx = np.min(pcd_np[:, 0])
    maxx = np.max(pcd_np[:, 0])
    miny = np.min(pcd_np[:, 1])
    maxy = np.max(pcd_np[:, 1])
    width = int((maxx - minx) / resolution) + 1
    height = int((maxy - miny) / resolution) + 1
    image = np.zeros((height, width, 3), dtype=np.uint8)
    for point in pcd_np:
        x, y, *_ = point
        r, g, b = point[-3:]
        pixel_x = int((x - minx) / resolution)
        pixel_y = int((maxy - y) / resolution)
        image[pixel_y, pixel_x] = [r, g, b]
    return image

很好,现在是时候进行测试了,你同意吗?为此,让我们加载一个点云数据集,将其转换为 numpy 数组,应用这个函数,并导出这个点云的图像:

#Reading the point cloud with laspy
pcd = laspy.read("../DATA/34FN2_18.las")

#Transforming the point cloud to Numpy
pcd_np = np.vstack((pcd.x, pcd.y, pcd.z, (pcd.red/65535*255).astype(int), (pcd.green/65535*255).astype(int), (pcd.blue/65535*255).astype(int))).transpose()

#Ortho-Projection
orthoimage = cloud_to_image(pcd_np, 1.5)

#Plotting and exporting
fig = plt.figure(figsize=(np.shape(orthoimage)[1]/72, np.shape(orthoimage)[0]/72))
fig.add_axes([0,0,1,1])
plt.imshow(orthoimage)
plt.axis('off')
plt.savefig("../DATA/34FN2_18_orthoimage.jpg")

这允许我们获得以下内容:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从 3D 点云到正射图像的工作流程。我们首先按照正射投影模式对点云进行投影,然后确保包括点到像素的映射用于反投影。© F. Poux

让我们继续球面投影

3.2 3D 点云球面投影

我们的旅程在遇到球面投影时变得非常有趣。这种技术提供了独特的视角,使我们能够通过“模拟”虚拟扫描站来可视化数据。为此,我们通过以下四个步骤进行: (1) 考虑 3D 点云, (2) 将这些点投影到球体上, (3) 定义一个几何体来检索像素, (4) “平展”这个几何体以生成图像。

🤠 Ville: 球面投影就像是在 3D 点云内部,拍摄你看到的 360 度照片

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3D 点云球面投影工作流。我们获取 3D 点云,创建一个 3D 投影球体,定义映射平面,并生成等距圆柱投影。© F. Poux

为了实现 3D 投影到球面,我们希望获得如下所示的点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如何将 3D 点云的点投影到球面。© F. Poux

然后,我们将根据几何形状(圆柱体)展开,以获得等距圆柱图像,如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如何从球面到等距圆柱图像。我们有一个投影机制,允许我们将像素“展开”到圆柱体上。© F. Poux

现在让我详细介绍允许实现这一点的功能:

def generate_spherical_image(center_coordinates, point_cloud, colors, resolution_y=500):
    # Translate the point cloud by the negation of the center coordinates
    translated_points = point_cloud - center_coordinates

    # Convert 3D point cloud to spherical coordinates
    theta = np.arctan2(translated_points[:, 1], translated_points[:, 0])
    phi = np.arccos(translated_points[:, 2] / np.linalg.norm(translated_points, axis=1))

    # Map spherical coordinates to pixel coordinates
    x = (theta + np.pi) / (2 * np.pi) * (2 * resolution_y)
    y = phi / np.pi * resolution_y

     # Create the spherical image with RGB channels
    resolution_x = 2 * resolution_y
    image = np.zeros((resolution_y, resolution_x, 3), dtype=np.uint8)

    # Create the mapping between point cloud and image coordinates
    mapping = np.full((resolution_y, resolution_x), -1, dtype=int)

    # Assign points to the image pixels
    for i in range(len(translated_points)):
        ix = np.clip(int(x[i]), 0, resolution_x - 1)
        iy = np.clip(int(y[i]), 0, resolution_y - 1)
        if mapping[iy, ix] == -1 or np.linalg.norm(translated_points[i]) < np.linalg.norm(translated_points[mapping[iy, ix]]):
            mapping[iy, ix] = i
            image[iy, ix] = colors[i]
    return image

🌱 Growing消化这个功能是至关重要的。它看起来很简单,但在多个阶段有一些巧妙的技巧。例如,你对 3D 点云到球面坐标步骤有什么看法?映射的作用是什么?在将点分配给像素时,使用映射作为条件语句的意义何在?

现在,为了使用这个方便的功能,我们首先加载并准备 ITC 室内点云:

#Loading the las file from the disk
las = laspy.read("../DATA/ITC_BUILDING.las")

#Transforming to a numpy array
coords = np.vstack((las.x, las.y, las.z))
point_cloud = coords.transpose()

#Gathering the colors
r=(las.red/65535*255).astype(int)
g=(las.green/65535*255).astype(int)
b=(las.blue/65535*255).astype(int)
colors = np.vstack((r,g,b)).transpose()

准备好后,我们可以定义投影所需的参数。这些参数包括投影中心(基本上是我们希望虚拟扫描站的位置)和最终图像的分辨率(以像素表示,即图像的最小边)。

resolution = 500

#Defining the position in the point cloud to generate a panorama
center_coordinates = [189, 60, 2]

最后,我们可以调用新的函数,绘制并将结果导出为图像。

#Function Execution
spherical_image, mapping = generate_spherical_image(center_coordinates, point_cloud, colors, resolution)

#Plotting with matplotlib
fig = plt.figure(figsize=(np.shape(spherical_image)[1]/72, np.shape(spherical_image)[0]/72))
fig.add_axes([0,0,1,1])
plt.imshow(spherical_image)
plt.axis('off')

#Saving to the disk
plt.savefig("../DATA/ITC_BUILDING_spherical_projection.jpg")

所有这些过程会产生以下图像:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3D 点云通过投影转化为等距圆柱图像。© F. Poux

你觉得怎么样?你可以调整各种参数,如分辨率或投影中心,以确保在“无数据”像素和相关全景之间取得良好的平衡。

🦊 Florent你刚刚解锁了一项强大的新技能——将 3D 点云转换为等距圆柱图像。确实,它允许你在你认为有意义的地方生成虚拟扫描,并开启使用图像处理和深度学习技术处理图像的可能性。你还可以将提供的功能扩展到其他映射投影,以增加你的工具库。

🤠Ville我几乎可以看到讲座大厅和我的办公室,荷兰的工作氛围!

3.3 3D 点到像素的映射

我们将原始点数据转换为结构化的栅格表示,理清看似散乱的信息。点云映射是我们在 2D 投影中处理 3D 点云的指南针。好消息是:我们已经处理了这个映射。

确实,如果你仔细查看函数generate_spherical_image,你会发现我们返回了mapping变量并将其捕获到另一个变量中以便后续处理。这确保了我们可以拥有一致的 3D 点到像素的映射。

4. 使用 SAM 的无监督分割

无监督分割以 Segment Anything 模型的形式出现。在非标记输出的情况下,我们通过 SAM 的分割架构,这属于聚类应用。这与大多数监督学习方法提供标记输出的方式相对,如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

无监督学习和监督学习之间的区别。在无监督学习中,我们旨在定义一些相似的数据“点”组,而在监督学习中,我们旨在满足监督需求(通常通过提供标记数据)。© F. Poux

因此,像素预测的转移,加上无缝的点云导出,展示了革新物体检测和场景理解等应用的潜力。

4.1. SAM 分割

要执行程序,我们可以重新执行我们用于测试 SAM 功能在 2D 图像上的代码片段,这些代码片段是:

sam = sam_model_registry"vit_h"
sam.to(device = USED_D)

mask_generator = SamAutomaticMaskGenerator(sam)

temp_img = cv2.imread("../DATA/ITC_BUILDING_spherical_projection.jpg")
image_rgb = cv2.cvtColor(temp_img, cv2.COLOR_BGR2RGB)

t0 = time.time()
result = mask_generator.generate(image_rgb)
t1 = time.time()

然后,我们可以在图像上绘制结果

fig = plt.figure(figsize=(np.shape(image_rgb)[1]/72, np.shape(image_rgb)[0]/72))
fig.add_axes([0,0,1,1])

plt.imshow(image_rgb)
color_mask = sam_masks(result)
plt.axis('off')
plt.savefig("../DATA/ITC_BUILDING_spherical_projection_segmented.jpg")

这将得到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3D 点云投影上的 Segment Anything 模型的结果。© F. Poux

这看起来我们正在勾画出图像中的重要部分。让我们继续前进。

4.2. 点预测转移

让我们用这张图像为点云上色。因此我们定义一个上色函数:

def color_point_cloud(image_path, point_cloud, mapping):
    image = cv2.imread(image_path)
    h, w = image.shape[:2]
    modified_point_cloud = np.zeros((point_cloud.shape[0], point_cloud.shape[1]+3), dtype=np.float32)
    modified_point_cloud[:, :3] = point_cloud
    for iy in range(h):
        for ix in range(w):
            point_index = mapping[iy, ix]
            if point_index != -1:
                color = image[iy, ix]
                modified_point_cloud[point_index, 3:] = color
    return modified_point_cloud

这意味着要为我们的点云上色,我们可以使用以下代码行调用我们的新函数:

modified_point_cloud = color_point_cloud(image_path, point_cloud, mapping)

这一行返回一个 numpy 数组,该数组保存了点云。

现在是 3D 点云导出的时候了!

4.3. 点云导出

要导出点云,你可以使用 numpy 或 laspy 直接提取一个.las 文件。我们将采用第二种解决方案:

def export_point_cloud(cloud_path, modified_point_cloud):
    # 1\. Create a new header
    header = laspy.LasHeader(point_format=3, version="1.2")
    header.add_extra_dim(laspy.ExtraBytesParams(name="random", type=np.int32))

    # 2\. Create a Las
    las_o = laspy.LasData(header)
    las_o.x = modified_point_cloud[:,0]
    las_o.y = modified_point_cloud[:,1]
    las_o.z = modified_point_cloud[:,2]
    las_o.red = modified_point_cloud[:,3]
    las_o.green = modified_point_cloud[:,4]
    las_o.blue = modified_point_cloud[:,5]
    las_o.write(cloud_path)

    print("Export succesful at: ", cloud_path)
    return

这样,我们可以导出我们的 modified_point_cloud 变量:

export_point_cloud("../DATA/pcd_results.las", modified_point_cloud)

在这一阶段,我们成功地获取了各种来自 3D 点云投影过程的 2D 图像。我们对其应用了 SAM 算法,基于其预测对其上色,并导出了一个彩色点云。因此我们可以开始获取一些关于我们所得到的东西的见解。

🦊 Florent为了快速在 Python 之外分析结果,我建议使用 CloudCompare 开源软件。如果你想要一个清晰的使用指南,可以阅读下面的文章。

## 3D 深度学习 Python 教程:PointNet 数据准备

《终极 Python 指南》用于构建大型 LiDAR 点云以训练 3D 深度学习语义分割…

[towardsdatascience.com

5. 定性分析与讨论

随着我们的旅程接近巅峰,现在是关注定性分析的时候了。特别地,我们不会进行定量分析,因为在这个阶段我们需要适当的标签。

🤠 Ville: 没有标签?你刚刚做的是零样本学习(砰!)或少样本学习(砰!砰!)。我们不能确定是哪种,因为我们不知道 SAM 的训练方式。因此对我们来说它有点像黑箱,但没关系。

我们仔细检查光栅和点云结果,得出的见解揭示了 SAM 的性能。同时,让我们保持脚踏实地,承认模型的局限性,同时展望未来。

5.1 光栅结果

SAM 在我们实施下的成果通过下述光栅结果得到了生动的展示。这些视觉效果作为画布,快速评估 SAM 的分割,帮助我们理解模型对场景的 2D 表示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Segment Anything Model 在 3D 点云投影上的另一个结果。© F. Poux

如你所见,即使在点分布不均和“黑区”下,SAM 仍能识别出点云的主要部分。具体而言,它可能突出了左侧的绿色区域,即危险材料所在的位置,以及为我们提取团队提供最直接路线的门窗!

5.2 点云结果

然而,正是在点云结果中,SAM 的真正能力得以显现。当我们在点云中穿梭时,SAM 的分割预测为经典的“点云混乱”带来了清晰度,展示了其在实际应用中的潜力。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Segment Anything 3D 进行的 3D 点云无监督分割结果。我们看到构成场景的主要元素的明显区别。© F. Poux

如我们所见,我们可以直接链接到基础点,这真是极其棒!想想这能为你的应用程序解锁什么。一个拥有不到五个主要断点的 100%自动化分割过程?不错!

5.3. 局限性

但,我们的探险只有在承认过程中存在的粗糙点后才算完成。SAM,尽管令人印象深刻,也不例外地存在局限性。通过认识这些不足,我们为改进和成长铺平道路。

首先是所有“未见”的点仍然保持未标记(下图中的白点)。这可能会成为完整处理的一个限制,如果你使用基本模型或大模型,你会看到比使用巨大模型时更多的未标记点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从中央视角 360°模拟扫描位置的第一次扫描中,未标记点与标记点的比例。 © F. Poux

此外,在这个阶段,我们使用了自动提示引擎,它触发了大约 50 个兴趣点,即分割任务的种子。虽然这对于获得直接结果非常好,但如果能够进行调整会更棒。

最后,此阶段的映射相对简单;它将大大受益于遮挡剔除和特定像素的点选择。

5.4. 视角

Segment Anything 模型只是 3D 点云分割更大领域中的一步。然而,如今的实现应该能够很好地适用于任何具有某种独特初始特征的 SAM 应用程序。正如下图所示,它也适用于俯视的航空点云。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Segment Anything 3D 在航空点云中的结果。© F. Poux

扩展到室内场景,你会发现也能得到一些相当不错和有趣的结果。这甚至对自动更换大厅灯具的灯泡是有用的,当然是由机器人自动完成的(更换一个灯泡需要多少机器人?)!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Segment Anything 3D 在另一个室内场景中的结果。© F. Poux

因此,除了泛化外,第一个视角是解锁生成全景图和融合不同视角预测的方法。当然,另一个视角是扩展到自定义提示,最终解决在 2D-3D 映射中提高点到像素精度的挑战。

结论

如果你是 13.37%中实际使代码正常工作的 3D 创作者中的一员,那么对你表示由衷的赞赏!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们在这篇文章中覆盖的工作流程。 © F. Poux

这是一个巨大的成就,你现在拥有了一个非常强大的工具来处理 3D 场景理解的语义提取任务。通过 Segment Anything 模型,你可以在许多产品中封装创新,改变我们感知和解读 3D 点云的方式。

我们的探索应该为这一开创性模型从起步到其影响描绘了一个全面、实用的图景。你现在可以探索这些变体,并根据之前部分发现的限制扩展其相关性。

🦊 Florent我期待你未来的项目能加以利用!

🤠 Ville继续编码!

参考文献

  1. Kirillov, A.,Mintun, E.,Ravi, N.,Mao, H.,Rolland, C.,Gustafson, L.,Xiao, T.,Whitehead, S.,Berg, A.C.,Lo, W.Y. 和 Dollár, P.,2023. 分割任何东西。arXiv 预印本 arXiv:2304.02643

  2. Poux, Florent,Mattes, C.,Selman, Z. 和 Kobbelt, L.,2022. 用于大规模点云分割的自动区域生长系统。建筑自动化138,第 104250 页。 Elsevier 链接

  3. Lehtola, Ville,Kaartinen, H.,Nüchter, A.,Kaijaluoto, R.,Kukko, A.,Litkey, P.,Honkavaara, E.,Rosnell, T.,Vaaja, M.T.,Virtanen, J.P. 和 Kurkela, M.,2017. 对选定的先进 3D 室内扫描和点云生成方法的比较。遥感9(8),第 796 页。 MDPI 链接

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

🔷其他资源

🎓作者推荐

要构建完整的室内语义提取场景,你可以将这种方法与“3D 点云形状检测用于室内建模”文章中解释的方法结合起来:

## 3D 点云形状检测用于室内建模

一份 10 步 Python 指南,用于自动化 3D 形状检测、分割、聚类和体素化…

towardsdatascience.com [## 3D 创新者通讯

每周提供实用内容、见解、代码和资源,以掌握 3D 数据科学。我写关于点云、人工智能…

learngeodata.eu](https://learngeodata.eu/3d-newsletter/?source=post_page-----80c06be99a18--------------------------------)

Segment Anything: 可提示的任意对象分割

原文:towardsdatascience.com/segment-anything-promptable-segmentation-of-arbitrary-objects-f28958c5612d

🚀Sascha 的论文俱乐部

Segment Anything 由 A. Krillov 等人

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Sascha Kirch

·发表于 Towards Data Science ·12 分钟阅读·2023 年 9 月 14 日

今天的论文讲解将是视觉化的!我们将分析 Segment Anything,这是 Meta AI 研究团队的一篇论文,它不仅在研究界引起了关注,也在各种深度学习从业者和支持者中引起了广泛关注。

Segment Anything 引入了可提示的分割任务,介绍了 segment anything 模型(SAM),并详细描述了生成一个包含超过 10 亿个掩膜的 1100 万张图片的新公开数据集。SAM 已被广泛采纳,并产生了一些新的最先进基础模型,如 Grounded-SAM,它将 Grounding DINO 与 SAM 结合起来。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源于 出版物Sascha Kirch

论文: Segment AnythingAlexander Kirillov 等人,2023 年 4 月 5 日

资源: GitHub演示项目页面数据集HuggingFace

类别: 分割、零-shot 预测、计算机视觉、提示、大规模

其他教程:

[BYOL] — [CLIP] — [GLIP] — [Depth Anything] — [DINO] — [DDPM]

大纲

  1. 背景与背景

  2. SAM — Segment Anything Model

  3. SA-1B — 具有 10 亿个掩码的数据集

  4. 实验与消融

  5. 结论

  6. 进一步阅读与资源

背景与背景

《Segment Anything》的作者明确声明:“我们目标是建立一个图像分割的基础模型。” 基础模型源于自然语言处理(NLP)的巨大成功。这些模型在自监督的方式下经过了大规模的训练。通常,这些模型在零-shot 任务中表现非常好,即它们可以解决与训练时不同的任务,并表现得相当不错,甚至比其监督型对手更优秀。近年来,许多研究人员致力于将 NLP 基础模型的成功带到计算机视觉等其他领域。

模型如 CLIP 和 GLIP 使得可以根据文本提示对图像分类或对象检测任务进行条件限制,而不是固定的类别集合。其他模型,如 BYOL 或 DINO,提出了不同的技术来学习输入图像的语义丰富表示,这也是许多计算机视觉应用的关键要求。

《Segment Anything》论文旨在:

  1. 通过提示启用零-shot 分割

  2. 训练一个大规模模型(SAM)作为演示模型

  3. 收集并发布最大的公开可用分割数据集。

但为什么零-shot 性能如此重要? — 答案有两个方面。首先,最初计算机视觉模型是以监督方式训练的,这不仅需要数据,还需要大量的真实标签。收集这些数据是极其耗时和昂贵的。其次,模型可以预测的类别仅限于训练时使用的固定类别集合。如果你想向模型中添加一个新类别,你需要首先收集数据并重新训练模型。

如何对分割模型进行提示? — 你可能对来自 ChatGPT、CLIP 或 GLIP 等模型的文本提示比较熟悉。虽然 SAM 原则上也经过了文本提示的测试,但它主要通过掩码、点、框或点网格来进行提示,如下图所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1:不同输入提示和生成的掩码。照片由Terence Burke拍摄,发布于Unsplash + 掩码由Sascha KirchSAM生成

了解了 SAM 的背景后,让我们转到重点,详细了解 Segment Anything Model,即 SAM。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Sascha Kirch

Sascha Kirch 的论文讲解

查看列表7 个故事外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

SAM — Segment Anything Model

Segment Anything Model(SAM)是一个多模态模型,它输入一张图像和一个或多个提示,并输出一个有效的分割掩码。该模型由三个主要模块组成:图像编码器、提示编码器和掩码解码器。

SAM 可以通过掩码、一组点、边界框或文本,或这些的任何组合来进行提示。

注意:尽管论文提到并实验了文本作为提示,但截至 2023 年 9 月,文本提示尚未在官方实现SAM 演示中发布。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2:SAM 架构。图片来源 + 注释由Sascha Kirch

图像编码器 — 为给定的输入图像输出图像嵌入。SAM 实现并适配了一个预训练的 ViT-H/16 掩码自编码器。这是一个相对较大的模型,性能强劲。

提示编码器 — 稀疏提示(例如点、框和文本)被转换为嵌入向量。文本提示在输入提示编码器之前,使用 CLIP 转换为文本嵌入。密集提示(例如掩码)则简单地通过步幅卷积下采样,并与图像嵌入相加。所有嵌入随后被送入最终阶段:掩码解码器。

掩码解码器 — 接受一组图像嵌入(可选地包含密集掩码嵌入)和一组提示嵌入,并输出有效的分割掩码。

还有两个细节我们应该讨论:提示的歧义性和性能。

简而言之,提示包含的上下文越少,就越模糊,对模型提供正确输出的难度也越大。对于文本提示,我们已经在 CLIP 和 GLIP 中看到了输入文本的具体性与模型性能之间的这种联系。同样,提供一个单点作为输入可能会产生多种可能的掩码。因此,SAM 输出一组三种掩码,分别对应于有效掩码的对象级别、部件级别和子部件级别,如下图所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3:单点提示的歧义性。图片来源 + 由 Sascha Kirch 注释

我想提到的第二个细节是推理速度方面的性能。你是否注意到图像编码器是 SAM 中最大的一部分?好吧,这个问题有点不公平,因为我之前没有告诉你,但 SAM 的设计目的是拥有语义丰富的图像嵌入(通常需要一个大型模型),然后通过一个轻量级的提示编码器和轻量级的掩码解码器来处理这些嵌入。好的一点是:每张图像只需运行一次图像编码器,然后可以使用相同的图像嵌入多次提示模型。这使得 SAM 可以在浏览器中运行,仅需 ~50ms 来预测给定提示的掩码(在图像嵌入计算后)。

让我们更详细地看看轻量级掩码解码器。它输入图像嵌入和提示嵌入,并输出一组带有相应分数的掩码。在内部,两个连续的解码器块通过自注意力和交叉注意力的组合生成图像与提示之间的强依赖关系。一个简单的上采样网络结合另一个交叉注意力块生成掩码和分数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4:掩码解码器的详细架构。 图片来源 + Sascha Kirch 的注释

SA-1B — 具有 10 亿掩码的数据集

Segment Anything 的第二个重大成果是创建和发布了一个大规模的分割数据集。它包含 1100 万张高分辨率和许可的图像,大约有 11 亿个掩码。虽然数据集的原始版本平均有 3300x4950 像素,但发布版本经过下采样,使最短边为 1500 像素。它在不同场景和每张图像掩码数量上都具有多样性,范围从不到 50 个到超过 500 个。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5:来自 SA-1B 的不同掩码。 图片来源 + Sascha Kirch 的注释

该数据集是在一个三阶段数据引擎中创建的,该引擎结合了人工标注和 SAM 生成的自动标注。

阶段 1:辅助手动阶段 — 一组专业标注员在 SAM 的早期版本的帮助下对图像进行了标注,SAM 在常见的分割数据集上进行了训练。他们被要求标注最显著的对象,并被鼓励在 30 秒后继续。在此阶段结束时,SAM 通过新的标签进行重新训练(总计 12 万张图像和 430 万个掩码)。

阶段 2:半自动阶段 — 在这一阶段的目标是通过首先让 SAM 预测一些掩码,然后让标注员标注缺少的、不太显著的对象,以增加掩码的多样性。在此阶段结束时,SAM 再次进行重新训练,包括新的样本(总计 30 万张图像和 1020 万个掩码)。

阶段 3:完全自动阶段 — 在这一阶段,注释完全自动化。SAM 通过 32x32 的网格点生成掩码,并应用一些后处理。

数据集分析

现在让我们仔细看一下论文中关于 SA-1B 数据集的一些分析。

在第一次评估中,作者创建了掩码中心点的标准化分布。有趣的是,这些分布会受到摄影师的偏差,即大多数照片将感兴趣的对象置于图像的中心和主轴上。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6:图像中对象中心点位置的分布。 图片来源 + Sascha Kirch 的注释

SA-1B 的一个主要优点是每张图像的掩码数量相比其他数据集更高(见图 7 左)。这也意味着 SA-1B 有许多小掩码(见图 7 中)。比较掩码的凹凸度,作为复杂性的衡量标准,SA-1B 与其他手动标注的数据集非常相似(见图 7 右)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7:SA-1B 的掩码属性与其他数据集的比较。 图片来源 + Sascha Kirch 注释

高度关注负责任的人工智能(RAI),在这里,不仅分析对某些人群的偏见,还尝试减轻这些偏见。如图 8 所示,世界上大多数国家的图像数量超过 1000 张,前 3 名国家来自不同地区。虽然低收入国家的样本相对较少(占所有样本的 0.9%),但绝对数量仍超过 900 万张,比其他分割数据集更多。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8:SA-1B 图像的估计地理分布。 图片来源 + Sascha Kirch 注释

作者进一步研究了感知性别展示、感知年龄组和感知肤色之间的性能差异。他们提供了预测掩码与真实掩码之间的平均 IoU(交并比)以及 95%的置信区间。SAM 的提示可以是单个点或三个点。主要信息是,在一个组内,结果非常相似(且置信区间重叠),这表明该组的任何成员都没有被偏袒。唯一的例外是感知年龄组中的老年人。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9:SAM 在感知性别展示、年龄组和肤色方面的分割性能。 图片来源 + Sascha Kirch 注释

## 每当 Sascha Kirch 发布新内容时获取电子邮件 🚀

每当 Sascha Kirch 发布新内容时获取电子邮件 🚀 想了解更多关于深度学习的知识或只是保持更新…

medium.com

实验和消融研究

Segment Anything 确实为我们提供了一系列实验,主要集中在其零-shot 性能上,因为这是作者的主要目标:找到一个可提示的零-shot 分割模型。同时,我们也知道其他模型如 CLIP 和 GLIP 的表现,提示调整几乎与模型微调在性能上等效。

为了进行实验,编制了一套包含 23 个多样化数据集的集合。它包含了来自各种数据分布的样本,如图 10 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10:来自 23 个数据集的样本。图片来源 + 注释由 Sascha Kirch

零-Shot 单点有效掩码评估

记住,零-Shot 意味着模型从未在评估过程中接触过的数据上进行训练。同样,单点提示由于其模糊性,如图 3 所示,是一项相当困难的任务。

在这个第一次实验中,作者将 SAM 与 RITM进行了比较,RITM 是一种强大的交互式分割器,作者表示其在他们的基准测试中表现最佳。

记住,当用单个点进行提示时,SAM 会输出 3 个不同的掩码及其相关分数。在这个实验中,选择分数最高的掩码进行评估。由于这种情况有时会出现错误,作者还对最佳掩码进行了评估,通过将预测结果与真实掩码进行比较,选择重叠度最高的掩码。这些是“oracle”预测。

在 23 个数据集中,SAM 在 16 个数据集中中的零-Shot 单点有效掩码预测中表现优于 RITM。在进行 oracle 预测时,它在所有 23 个数据集中都优于 RITM。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 11:在 23 个数据集上的 SAM 与 RITM 对比。图片来源 + 注释由 Sascha Kirch

零-Shot 文本到掩码

在这个实验中,SAM 通过文本进行提示。作者将此功能称为概念验证,因此既没有进行广泛的实验,也没有在其官方代码实现中发布此功能。

看图 12,你可以看到 SAM 能够为像“海狸牙齿格栅”这样的复杂对象返回正确的掩码。在其他一些情况下,模型仅通过文本提示失败,他们展示了在提供点的上下文时,SAM 能够正确预测单个或多个擦拭器,显示出不仅点被用于预测,文本也被考虑在内。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 12:零-shot 文本到掩码。图片来源 + 注释由 Sascha Kirch

零-Shot 边缘检测

有趣的是,SAM 也可以用于边缘检测,这是一项它在训练过程中未被考虑的任务,也没有访问相关数据。

为了预测图像,SAM 首先使用 16x16 点的网格进行提示,生成 768 个预测的掩码(每个 256 个点的对象、部分和子部分)。然后对生成的掩码进行筛选和后处理,以获取边缘掩码。

如图 13 所示,与真实数据相比,SAM 预测了更多的细节。但为了公平起见,如果真实数据不完整或覆盖了不同的抽象层次,这种比较对我来说似乎不太公平。但总的来说,性能还是相当不错的!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 13:SAM 的零样本边缘预测。 图片来源 + 注释由 Sascha Kirch

零样本实例分割

对于这个实验,SAM 以 COCOLVIS 上训练的完全监督的 ViTDet-H 的边界框输出作为提示。然后将生成的掩码连同初始边界框一起输入到 SAM 中,以精细化结果。图 14 显示了 ViTDet 和 SAM 的比较。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 14:在 LVIS v1 上的零样本实例分割。 图片来源 + 注释由 Sascha Kirch

这里有两件事需要注意:如果你查看 COCOLVIS,你会发现掩码与对象的像素对齐并不完全。这种偏差在 ViTDet 中也存在,这就是为什么 SAM 的质量似乎更好的原因。由于基准真实值具有相同的偏差,而与差的 GT 相比,SAM 的表现可能更差。因此,他们要求人工进行视觉检查。其次,为什么这只大象只有 3 条腿 😅。无论我怎么努力,我都看不到第四条腿…

消融实验

在消融实验部分,作者主要关注于扩展数据集、提示点数量和图像编码器的大小(见图 13)。性能以平均 IoU 报告。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 15:消融研究。 图片来源 + 注释由 Sascha Kirch

有趣的是,尽管数据扩展和模型规模扩展影响了 mIoU 性能,但它达到饱和状态。这可能表明模型已经足够好,没有太多改进的空间,或者可能是他们方法的局限性。

结论

Segment Anything 引入了可提示的 Segment Anything Model (SAM) 以及一个包含超过 10 亿个掩码的分割大规模数据集,涵盖超过 1100 万张图像。能够提示分割模型带来了很多灵活性,比如将训练好的模型适应于未见过的任务或检测未知类别。虽然有些人讨论 SAM 是否可以被视为基础模型,因为它是以监督方式训练的,但它仍然显示出了显著的成果,并已被广泛采用。

进一步阅读与资源

正如你自己可能知道的那样:深度学习领域正在以令人难以置信的速度发展。因此,SAM 发布后,许多新项目在其成功的基础上进一步改进了预测质量、减少了推理时间,或者使模型适用于边缘应用,这也就不足为奇了。

以下是一些有趣的资源,它们在 SAM 的基础上进行扩展:

  1. 基础分割任何内容

  2. 高质量分割任何内容

  3. 快速分割任何内容

  4. 更快的分割任何内容:朝着适用于移动应用的轻量级 SAM

在这里,我分享一些链接,如果你想亲自体验 SAM 和 SA-1B:

这里是我一些文章的链接,带你了解一些相关的基础模型:

## CLIP 基础模型

论文总结— 从自然语言监督中学习可迁移的视觉模型

towardsdatascience.com ## GLIP: 将语言-图像预训练引入物体检测

论文总结:基础语言-图像预训练

towardsdatascience.com ## BYOL - 对比自监督学习的替代方案

论文分析—Bootstrap Your Own Latent: 自监督学习的新方法

towardsdatascience.com

将文本分段成段落

原文:towardsdatascience.com/segmenting-text-into-paragraphs-e8bed99b6ebd

基于监督学习的统计 NLP 方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Arun Jagota

·发表于 Towards Data Science ·11 min 阅读·2023 年 2 月 25 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Gordon Johnson 提供,来自 Pixabay

在之前的 Medium 文章中,我们讨论了将文本分割成句子的问题[3]。现在我们来看一个相关问题:将文本分割成段落。

初看起来,这两个问题似乎本质上是相同的,只是在不同的分块层次上。实际上,将文本分段成段落的问题要有趣得多。

一方面,句子边界有明确的信号,如句号、问号或感叹号。通常,问题在于这些标记中的哪些是实际的边界,哪些是在句子内嵌入的边界。这就是假阳性的难题。

将文本分段成段落更为复杂。这样考虑一下。假设你有一个长句子序列,没有段落分隔符。应该在哪里设置段落边界?这不是一个容易解决的问题,也不一定有唯一的解决方案。这意味着可能存在多种将句子序列分段的合理分法。

将文本分段成段落可以看作是文本分割的一个特例[1]。文本片段是一个连续的段落,保持一定的连贯性,比如说在同一主题上。根据这种连贯性度量,段落会在主题发生变化时过渡到另一个段落。

更广泛的文本分段问题更难解决。原因有几个。其中之一是很难获取标记数据。对于段落分段,有大量的标记数据可用,这些数据以网页和包含段落分隔符的维基百科文章的形式存在。而对于更广泛的文本分段问题情况则不然。

在这篇文章中,我们认为一个能够建议合理分割边界的算法对写作中的人是有帮助的。就像 Grammarly 对写作的帮助一样。换句话说,精确率和召回率都不需要特别高。精确率需要合理;召回率甚至可以更低。

本文的其余部分应以此视角进行阅读。我们将满足于一个具有合理精确度的解决方案,可能接近 50%,以及较低的召回率,可能接近 10%。关键是即使这样也在类似 Grammarly 的环境中是有用的。

即使段落分割建议很少出现,只要它们具有合理的精确度,它们就会增加像 Grammarly 这样的产品的价值。

不用说,如果我们可以在付出最少努力的情况下获得更好的精确率或召回率,我们自然会选择这样做。

预测段落分割的概率模型

我们将从正式描述开始,用简单的英语解释其各个组件。

设 X1 和 X2 表示训练语料库中相邻的两个句子。

我们将与(X1, X2)关联一个二进制标签 Y。如果 X1 和 X2 之间有段落分割,则 Y 为 1,否则为 0。

我们将跟踪第三个预测变量i。X1 将是当前段落中的第i个句子。预测变量“i”将赋予我们的模型关注段落长度的能力。

我们的训练集将包含实例(X1, X2, i, Y)。

从训练集中,我们旨在学习一个模型P(Y | X1, X2, i)。

P(Y=1 | X1, X2, i)将表示在 X1 作为当前段落中的第i个句子的情况下,X1 和 X2 之间有段落分割的概率。

P(Y=0|X1, X2, i)表示在 X1 作为当前段落中的第i个句子的情况下,X2 应该扩展当前段落的概率。

模型 P(Y | X1, X2, i)非常复杂。这是因为 X1 和 X2 是句子,可能非常稀有或很长。这意味着,即使我们的训练集包含了几亿个标记实例,也可能没有足够的数据来估计这个模型。

我们需要做一些假设。

首先,让我们应用贝叶斯规则。

P(Y | X1, X2, i) = N(X1, X2, i, Y)/Z

其中 N(X1, X2, i, Y)等于P(X1, X2, i | Y) P(Y)。

Z 只是 N(X1, X2, i, 0) + N(X1, X2, i, 1)。

接下来,我们将对 N(X1, X2, i, Y)进行如下分解。

N(X1, X2, i, Y) = P(X1 | Y)*P(X2 | Y)*P(i | Y)*P(Y)

P(X1 | Y=1)是段落中最后句子的分布。P(X1 | Y = 0)是段落中非最后句子的分布。

P(X2 | Y = 1)是段落中第一句子的分布。P(X2 | Y = 0)是段落中非第一句子的分布。

现在考虑P(i | Y)。

让我们提醒自己 X1 是当前段落中的 i 句。因此,P(i | Y=1) 实际上是段落长度的分布,因为段落必须在 X1,即当前段落中的第 i 句之后结束。

P(i | Y = 1) 将倾向于偏向于较小的 i。这是因为大多数段落较短。

P(i | Y = 0) 将倾向于偏向于更小。这是因为 Y = 0 表示当前段落中的第 i 句 X1 不会结束该段落。

概率模型 P(X1 | Y) 和 P(X2|Y) 都仍然过于复杂。这是因为句子的宇宙是无限的。也就是说,句子可以任意长。也可以任意稀有。

我们可以做进一步的简化假设吗?具体来说,使用的可能性不一定是整个句子,而是它们的前几个词。

让我们从实际例子开始。

首先,让我们看看继续短段落的句子的例子。

假设下一句以“例如”,“例子”,“更准确地说”等开头。如果当前段落足够短,比如一两句,这些前缀在下一句中的出现预测 Y = 0,即扩展段落。

为了支持这个假设,我们邀请读者阅读这些相邻句子的对,例如 en.wikipedia.org/wiki/Deep_learning

深度学习算法可以应用于无监督学习任务。这是一个重要的好处,因为未标记的数据比标记的数据更为丰富。例子 包括可以以无监督方式训练的深度结构,如深度置信网络。

深度学习是一类机器学习算法,它[8]: 199–200 使用多个层次逐步从原始输入中提取更高级的特征。例如,在图像处理过程中,较低的层次可能识别边缘,而较高的层次可能识别对人类相关的概念,如数字、字母或面孔。

“深度学习”中的“深度”指的是数据转换通过的层数。更准确地说,深度学习系统具有实质性的信用分配路径(CAP)深度。

你是否同意每个加粗的词序列预测段落的延续?

这些例子表明,考虑简化 P(X1 | Y) 为 P(X1 的前几个词 | Y) 是有意义的。

这就引出了“few”在这里的值是多少的问题?我们稍后会解决这个问题。

接下来,让我们看看一句话段落的例子。

为此,我要求 ChatGPT 给我一些一句话段落的例子。似乎它很字面地理解了这个问题。因此我重新表述了问题为

给我一些由一句话构成的段落的例子。

现在我得到了好的例子。

沉默。

停止。

再也不见。

为什么?

是的!

对不起。

足够了。

记住。

帮助!

再见。

我们期望这些句子中的每一个都有较高的 P(X1 | Y = 1) 的可能性。也就是说,对于其中一些或全部句子,P(X1 | Y = 0) 也可能相对较高。这意味着段落不会在它们之后立即结束。

尽管如此,我们在这里展示这些示例,因为它们确实表明 P(X1 | Y = 1) 对于这些句子是值得建模的。

接下来,让我们看看以新段落开始的句子的示例。我们从 en.wikipedia.org/wiki/Deep_learning 中挑选了一些段落,并展示了它们第一句话的前几个词。

深度学习是一个更广泛领域的一部分…

深度学习架构如…

人工神经网络(ANNs)是…

在深度学习中,每一层学会…

ANN 基于一组…

深度神经网络(DNNs)可以建模复杂的非线性…

这些示例表明,P(X2 | Y = 1) 可以简化为

P(X2 | Y = 1)

对于“少量”这一选择的合适情况。

让我们将从这些示例中学到的内容形式化。

我们可以简化 P(X1 | Y) 和 P(X2 | Y) 为

P(X1 以 w(1)、w(2)、…、w(k) | Y)

以及

P(X2 以 w’(1)、w’(2)、…、w’(k’) |Y)

分别。

这里 w(1)、w(2)、…、w(k) 和 w’(1)、w’(2)、…、w’(k’) 分别是 kk’ 的词序列。

显而易见的问题是 kk’ 应该是什么?

解决这个问题的一种方法是不要提前固定 k 和 k’,而是推迟决策直到推断时。

方法如下。

首先介绍一些术语。我们将称以句子开始的词序列为句子的前缀。

现在考虑 P(X1 | Y=y)。我们将按如下方式近似。

我们将首先找到 X 的最大前缀,称之为 P,并且具有足够的支持。我们将使用 P(X1 以 P 开始 | Y) 作为 P(X1 | Y) 的代理。

X1 前缀 P 对 P(X1 | Y) 的支持定义为训练集中 P 作为 X1 前缀的实例数量。

这一近似过程的理念是,我们应该使用 X1 的最长前缀,只要它在训练集中出现的次数足够多(作为 X1 的前缀)。

类似地,我们应该将 P(X2 | Y) 估计为 P(Q | Y),其中 Q 是 X2 的最大前缀,其支持度足够大。

我们推断出的形式对模型意味着什么?我们需要跟踪所有 X1 的前缀 P 的概率 P(X1 以 P 开始 | Y)。P(X2 | Y) 也类似。

内部,对于建模 P(X1 |Y) 和 P(X2 | Y),我们需要跟踪大量的词序列。

幸运的是,这些词序列可以收集到所谓的 Trie 数据结构中。这些结构被优化为紧凑地表示大量的词序列。

这些 Tries 在训练过程中如下构建。

我们将分别使用四个 Tries T10、T11、T20 和 T21。Tiyi = 1 或 2,将存储前缀序列及其对 Y=y 的计数,针对 Xi

Trie 中的每个节点将存储一个计数。

我们将所有的 Trie 初始化为从一个单独的节点开始,即根节点,其计数设置为零。

现在考虑一个训练集中的实例 (X1, X2, y)。我们忽略了 i,因为它不会影响 Trie。

将 X1 解释为单词序列时,我们将在 T1y 中查找 X1,必要时扩展 Trie 并添加由新节点组成的路径。每次创建新节点时,其计数将初始化为 0。

现在,在 Trie T1y 中,我们将沿着表示 X1 的路径递增所有节点的计数,每个节点增加一次。

处理 X2 时,我们将在 Trie T2y 上重复相同的过程。

数值示例

现在让我们说明这个过程。

四个 Trie 将被初始化为 T10、T11、T20 和 T21,每个 Trie 的初始状态为 {[]:0}。

现在假设我们呈现第一个训练实例为 ([a,b],[A,B,C],1)

T11 的新状态将是 {[]:1,[a]:1,[a,b]:1}

T21 的新状态将是 {[]:1,[A]:1, [A,B]:1, [A,B,C]:1}

现在假设我们呈现这个训练实例:([a,d],[A,B,E],1)

T11 的新状态将是 {[]:2,[a]:2,[a,b]:1,[a,d]:1}

T21 的新状态将是 {[]:2,[A]:2, [A,B]:2, [A,B,C]:1,[A,B,E]:1}

在上述示例中,为了视觉上的方便,我们将每个 Trie 表示为一个哈希映射,即 Python 中的字典。

实际上,我们可以通过利用(重复的)前缀结构将 Trie 更紧凑地表示为树。

将 Trie 表示为树对于查找与给定序列 X 的所有前缀相关的计数也更高效。我们只需沿着 Trie 包含的唯一路径向下查找 X 的最长前缀。我们说“最长前缀”是因为 X 可能未完全包含在 Trie 中,如果 X 在训练集中从未以这种上下文出现,它会被放置在这个 Trie 中。然而,Trie 中总是存在至少一个 X 的前缀的路径,即使只是空前缀。

使用 Trie 推理

假设训练已经完成。现在,对于给定的 (X1, X2, i),我们想计算 P(Y=y|X1,X2,i)。

这个计算中涉及 Trie 的部分是 P(X1|Y=y) 和 P(X2|Y=y)。

让我们演示如何计算其中之一,其他的计算过程也会类似。

让我们选择 P(X1|Y=y)。

我们遍历 Trie T10 和 T11 以找到 X1 的最长支持前缀。我们需要使用这两个 Trie,因为 X1 的前缀 P 的支持是 T10 和 T11 中 P 结束的节点上的计数之和。

让我们用 P(X1) 表示 X1 的最长支持前缀。

P(P(X1) | Y=y) 只是 P 在 Trie T1y 中结束的节点上的计数除以 Trie T1y 根节点上的计数。这仅仅是训练集中标签为 y 且 X1 以 P(X1) 开始的实例数量,除以标签为 y 的训练集中实例数量。

总结

在这篇文章中,我们介绍了将文本分割成段落的 NLP 问题。我们注意到,这个问题比将文本分割成句子的难度更大,但比将文本分割成以主题为单位的连贯单元的难度要小。

我们将这个问题框架设定为监督学习问题。有大量标记数据可以直接使用。输入是一对相邻句子。结果是这两句之间是否存在段落分隔。因此,这是一个监督学习问题,其中输入是一对序列,结果是二元的。

接下来,我们在简单贝叶斯假设下应用了贝叶斯规则,即在给定结果的情况下,预测变量的条件独立性。然后我们计算了结果公式中的似然性和先验项。

从这里我们注意到,即使在简单假设下,得到的模型也过于复杂。我们讨论了如何通过将输入的两个句子建模为从空前缀到整个序列的一组前缀来应对这种复杂性。在推理时,我们描述了如何使用“正确”的前缀来预测结果。

我们检查了多个实际文本中相邻句子的实例,以支持我们基于前缀而非完整句子的工作方法。

最后,我们注意到,处理句子的所有前缀而不是句子本身可能会导致模型规模激增。为此,我们提出了一种使用 Tries 的方案。在适当的 Tries 中,以紧凑方式表示相同上下文中的序列。我们详细讨论了 Tries 如何在训练过程中学习,以及 Tries 在推理过程中如何使用。

参考文献

  1. 文本分割的神经模型

  2. Grammarly

  3. towardsdatascience.com/segmenting-text-into-sentences-using-nlp-35d8ef55c0fd

  4. en.wikipedia.org/wiki/Trie

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值