TowardsDataScience 2023 博客中文翻译(二百三十五)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

多区域数据源

原文:towardsdatascience.com/multi-regional-source-of-truth-d43e1cc9e098?source=collection_archive---------17-----------------------#2023-03-14

以 BigQuery 作为数据仓库的多区域 BI 解决方案

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

·

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

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

图片由 Lars Kienle 提供,来源于 Unsplash

我在一年前创建了一篇关于从 Postgres 迁移到 Snowflake 的文章,现在这里有另一个迁移案例。这一次我将集中于业务案例、架构和设计,而不是技术方面,但我也会尝试分享一些可能有用的技巧。

业务案例

这是一个 B2B(企业对企业)业务。公司是一个 AaaS(应用即服务)提供商。它的软件下载作为在线服务,供全球多个国家的客户使用。它主要需要一个 BI(商业智能)系统来为客户提供数据访问。以下是最有趣的要求:

  • 一些客户需要访问现成的仪表板和可视化,而有些客户希望将数据提取到他们的 DWH 中。

  • 由于客户在不同国家运营,数据应保持在最初创建的区域内。

  • 客户应该只能看到他们的信息。对于相同的区域,数据存储在同一数据库和相同表中,因此必须应用行级安全。

  • 在大多数情况下,客户希望实时数据。

让我先介绍一下现有架构的样子:

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

作者提供的图表

关键特性是读取只读副本以提取数据,没有 DWH,并且在其上使用 Looker。显而易见的优点是这是设置最快的解决方案。副本通常具有实时数据。Looker 负责数据转换、语义层和可视化。Looker 还通过不同的过滤器实现行级安全。不幸的是,这可能是唯一的优点,它还有许多缺点。

  • 应用任何现代数据工程技术的能力非常有限。我们不能保存转换后的数据,不能使用适当的工具进行转换、实现管道、自动测试等。基本上,唯一可用的工具是 Looker Derived Tables 功能。

  • 由于没有 DWH,无法从其他来源(如 CRM)提取数据,也无法混合来自不同服务的数据或上传任何静态字典。BI 系统仅限于由特定软件产品保存的数据。

  • 如我之前提到的,一些客户需要提取数据,并希望获得尽可能低级和原始的数据。Looker 在提供报告方面做得很好,但它在其语义层之上工作,语义层已经有一些联接,并且并非所有字段都可用,每个 Looker 探查都是为特定业务流程设计的。结构并未针对导出进行优化。

  • 性能。这可能是最大的问题,因为 OLTP 数据库不适合分析用途,并且由于我们使用的是只读副本,无法控制索引。这不仅导致初始性能缓慢,还会导致性能下降,因为没有索引时,数据越多查询速度越慢。

解决方案

解决这些缺点的最直接方法是引入 DWH 层,我们确实这样做了。新的设计如下所示:

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

作者提供的图表

流数据

市场上有多种流媒体提供商可供选择,我们选择了 Fivetran,但也可以看看 Stitch、Airbyte 或其他开源框架,如 Meltano 或 Singer。我们也考虑过 Google Storage Transfer Service,但它还处于原始状态,灵活性不足。

流媒体设置的整个过程当然不是没有问题的。以下是其中一些问题:

  • OLTP 数据库没有公共访问权限,因此必须为 Fivetran 设置 VPN 以拉取数据。这涉及一些网络配置,并需要反复讨论以决定既适用于 Fivetran 又适用于我们的解决方案。

  • Fivetran 增加了数据库的负载,因此我们必须设置额外的监控,确保流媒体不会影响主要功能。

  • 最初的计划是每 5 分钟同步一次数据,但由于更改量巨大,导致连接超时并在日志中抛出错误。Fivetran 能够恢复,因此没有数据丢失,但生成了混乱,使得很难区分实际错误和这些过载错误。没有简单的解决方案,只能放宽对实时数据的要求,减少数据拉取的频率。

转换数据

选择 BigQuery 主要是因为它满足了我们所有的要求,并且大多数现有基础设施也在 Google 云中,因此过渡本应顺利。

由于我们现在有了可读写的 DWH,我们能够利用 DBT 工具进行数据转换,并将来自不同来源的数据混合在一起。即使没有任何转换,通过 Looker 查询之前相同的数据,但从 BigQuery 中获取,性能也有了显著提高,有时对重表的提升超过了100 倍

我不会详细讨论 Looker 的数据准备,因为它非常领域特定。更有趣的是我们如何解决数据导出问题。总的来说,这很简单,我们只是给了客户对 BigQuery 的直接访问权限,但细节中的问题需要关注,所以让我分享一些细节。

实现的最简单要求是将数据保存在不同的区域。幸运的是,BigQuery 中每个数据集的区域是可配置的。Fivetran 将数据流入不同的数据集,而 DBT 模型则针对这些数据集分别运行,因此来自不同区域的数据完全不会混合。

实现行级安全性以确保客户仅能访问他们的数据要复杂一些。BigQuery 支持行级安全性,但由于我们使用 DBT,因此选择了不同的方法,以更好地控制数据暴露和更多自动化选项。

我们创建了一组 DBT 模型,这些模型根据当前连接用户的信息在 SQL 代码中应用安全性,并在 BigQuery 中创建视图。更准确地说:

  • 需要访问的每个客户都提供用户/服务帐户。IAM 角色具有对 BigQuery 的通用访问权限,但没有对任何特定数据集的访问权限,因此,初始状态下,新用户无法查询数据

  • 新用户电子邮件与客户识别信息一起添加到 DBT 种子文件中。不同地区的文件分别保存。这提供了用户-客户映射以及用户应该访问的数据集的信息。种子文件存储在 GitHub 中,因此我们可以在此文件的每次更改时运行 GitHub 操作

  • GitHub Action 在不同地区运行参数化的 DBT 项目。地区和数据不同,但结构相同,因此我们只需为每个视图编写一次 SQL 代码。DBT 上传种子文件,创建视图,最后一步是为各个用户提供对所需数据集的只读访问权限

  • 视图通过session_user()函数识别当前登录的用户电子邮件,将其与用户-客户映射连接,并筛选出与客户无关的所有内容。

这里有一个我想分享的非常具体的情况,即授予 oAuth 用户访问权限。在 Google IAM 中有两种类型的用户:服务帐户和 oAuth 用户。处理第一种用户比较简单,但并非所有下游系统都支持这种访问方式。例如,Tableau online 无法使用服务帐户。第二种类型是普通用户登录 Google 帐户的方式。有关如何确保这种用户仅访问特定数据集的文档非常有限,因此这里是一些提示:

  • 用户应该拥有BigQuery Job用户的所有权限,并额外具有bigquery.jobs.create权限

  • 数据集级权限可以像服务帐户一样授予,例如使用 SQL GRANT 命令

  • 用户不一定需要由相同的 Google 帐户创建。例如,来自其他组织的人可以获得访问权限,只要他们的 IAM 权限在我们的项目中配置正确

  • 小心组电子邮件。虽然数据集级别的访问权限正常工作,但session_user()返回的是用户的个人电子邮件,因此行级安全性无法与组电子邮件一起使用

最后一个要提到的提示是,由于我们客户端只能访问视图而不能访问表格,因此视图应该能够从其他数据集查询数据,而无需将原始数据暴露给客户端。BigQuery 具备这样的能力,可以允许一个数据集中的所有视图查询另一个数据集中的数据,而不受最终用户权限的限制。

BigQuery 监控

由于客户可以直接访问 DWH,因此了解每个客户如何使用这些功能非常重要。由于每个客户都与用户绑定,因此在用户级别的监控应该能够回答这个问题。我遇到了一篇关于如何做到这一点的有趣文章,我们刚刚在 Looker 中重新实现了相同的方法。

基础设施的另一个部分是 Github,更准确地说是 GitHub Actions。此功能允许创建执行工作流并设置计划。像这个这样的开源操作可以帮助实现这一点,唯一需要支付的就是 GitHub 的使用时间。

单一真实数据源

我最后要提到的是解决方案的灵活性。我已经描述了主要和最有趣的案例,但公司还有其他服务,凭借当前的解决方案,我们能够将所有这些服务的分析转移到我们的 DWH 系统中,并将其用作下游系统的真实数据源。换句话说,它也作为数据馈送存在。DBT 被用来为所有业务案例生成视图和表格,而数据集级别的访问允许在数据级别分离下游系统和用户。

DBT 的灵活性允许在一个项目中为不同的下游系统创建模型,这一过程通过前面提到的 GitHub 实现自动化。

多阶段推荐系统构建方法

原文:towardsdatascience.com/multi-stage-approach-to-building-recommender-systems-71a31e58ecb4?source=collection_archive---------4-----------------------#2023-01-01

发现构建推荐系统的最先进方法

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

·

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

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

多阶段推荐系统。图像来源 作者

# 1 信息超载的问题

我们都对搜索引擎和推荐系统并不陌生。没有它们,我们会被每秒钟产生的大量信息所淹没。这些信息可能有不同的数据格式 —— 文本图像音频视频 等。

从根本上讲,这些系统可以在拥有大量信息目录的情况下,根据用户的查询或个人资料,呈现、过滤和排序相关项目,使我们能够在信息的海洋中进行导航,否则用户会因信息过载而挣扎。

如本帖标题所提,我们将重点讨论推荐系统。我可能会在未来的文章中单独介绍搜索系统设计,请关注。

# 2 个行业推荐系统的用例

你需要一个推荐系统吗?

公平地说,80% 的公司不会有建立复杂推荐系统的需求。对于小型目录和少量产品类别,使用动态 SQL 查询就足够了。

如果你想例如,在项目描述页面上建立一个 “类似的项目…”“你可能也喜欢…” 面板,你会编写一个 SQL 查询,以检索当前 类别top xx items

如果你想在网站首页上建立一个 “为你推荐…” 面板,或在营销邮件中发送推荐项目列表,同样适用。只需检索用户在历史时间段内查看的顶级项目类别,并编写相同的动态 SQL 查询以基于用户的历史互动数据检索推荐项目。

探索你的网站的用户也能够扫描整个目录,以寻找他们需要的内容,而不会感到不知所措。

影响是否需要推荐系统的因素

项目目录大小:

  • 随着目录规模的增加,通过手动过程标记每个项目的元数据变得更加繁琐和昂贵。

  • 在市场等商业模式中,其中项目列表完全由用户众包,确保高质量的项目元数据标签几乎是不可能的。

  • 我们必须寻找生成推荐的替代方法,而不仅仅是使用简单的 SQL 查询。

缺乏结构化数据:

  • 对于某些公司,表格格式的结构化数据可能甚至没有被收集。相反,他们可能拥有一个包含视频、图像、自由文本、文章的存储库,这些可以被挖掘用于生成推荐。

  • 使用非结构化数据作为该想法的基础需要采用深度神经网络方法,将非结构化数据编码为结构化的数值数据,以学习有意义的表示。

  • 结构化 — 表格格式的数据,具有明确定义的模式。

  • 非结构化 — 文本图像音频视频

可重复的业务指标改进过程:

  • 如果没有可重复的实验过程,就无法测试新技术来改进对用户的推荐。此外,也无法监控系统更改是否对关键业务指标产生了实际影响。

  • 推荐系统已被证明能够改善业务指标,例如点击率、转化率、加购物车购买、新项添加到购物车、收听/观看时间等。

  • 实施第一个推荐系统并不一定能保证你会有一个可重复的实验过程开始,但它是迈向这一目标的第一步。

推荐系统案例研究

以下是一些社会中普遍存在的推荐系统示例用例,按行业分类:

行业中的推荐系统使用案例:

在线教育、在线银行、保险也是我期望找到成功案例的行业,但信息很少。

构建和设计推荐系统

在现实世界中,IT 系统通常是多个服务共同工作的集合。构建推荐系统是一种分层、多阶段的过程,每个阶段使用适当类型的模型。

让我们来看看机器学习工程师在构建推荐系统时所经历的阶段。

# 3 推荐系统设计

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

多阶段推荐系统。图片由 作者 提供。

上图展示了构建推荐系统的事实标准。最先进(SOTA)的推荐系统将系统工程分解为这个三阶段的过程。

关于模型服务的说明: 在每个阶段,我们还需要考虑如何暴露或服务已经为下游消费者构建的机器学习模型。

我们将在后续部分中探讨每个阶段。

候选生成;检索

候选生成在一些推荐系统文献中也被称为检索阶段。项目目录可能会增长到数十万、数百万或数十亿项。

候选生成方法能够通过高效比较项目之间的相似性,使用距离度量选择前一百个最相关的项目。

首要的想法是学习用户和项目的数值向量表示,即输入数据。这些向量,称为嵌入,可以被写入数据库或最近邻索引中,后续系统可以从中检索它们。

一旦我们有了用户和项目的嵌入,就可以使用距离度量如欧氏距离、点积、余弦相似度等计算它们在嵌入空间中的相似性。

候选生成旨在通过一种高效的方法来缩小潜在候选的数量,该方法返回数百个候选结果,并在下一阶段进行排序。通常,会建立多个候选生成器,这些生成器使用不同的方法进行训练,每个生成器利用其特定的数据集。

为什么不直接进入评分阶段?

一个常见且合理的问题是,为什么不直接进入评分阶段?为什么要经过生成嵌入的过程来进行候选生成?

这里有几个需要考虑的要点:

  1. 生成嵌入是一个关键的特征工程步骤。这些向量值可以在下游作为输入特征值重复使用,以进一步提高下游评分模型的准确性。

  2. 候选生成模型可以在非结构化数据上进行训练,并使用多维向量表示项或用户的非结构化数据属性。这些有用的信息,如文本图像音频视频,否则将被忽视。

  3. 嵌入空间中每个项或用户的语义视觉理解。你可以使用如TensorBoard Embedding Projector这样的工具在二维空间中可视化高维向量。这样,你也可以观察,并应用聚类算法发现相似对象的簇。

  4. 候选生成模型的输出旨在建立项或用户之间的相似性。这个想法在大多数推荐系统产品中是基础的。例如**“类似于…”“与你相似的用户也喜欢…”**。

  5. 高效服务模型结果。使用近似最近邻(ANN)引擎来服务候选生成模型也很高效。有关 ANN 算法的更多信息,请参阅视频:Approximate Nearest Neighbors: Data Science Concepts与评分阶段对比——在评分模型训练过程中,你还需计算所有工程特征,API 才能返回模型结果。

如前所述,服务这些嵌入是通过暴露 ANN 引擎来完成的。市场上有许多选择,包括开源和公共云,以下是一些选项:

只需使用这些服务中的一个,选择你的距离相似性度量,下游消费者可以调用 API 以检索对象嵌入。

另一种方法是将对象嵌入写入数据库,下游系统查询数据库以检索这些嵌入。这本质上就是Spotify 使用 Cloud Bigtable所做的。

评分;排名

评分在一些推荐系统文献中也被称为排名阶段。它是在候选生成之后发生的阶段。下图展示了这一过程的可视化表示:

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

多候选生成模型。图片由作者提供。

在从多个候选生成模型中检索出前一百个候选项后,每个候选项的嵌入可以与有关项目和用户的其他输入数据(如日期时间、类别、设备类型等)一起组织成结构化的表格格式。评分是一个监督学习问题,因此我们必须确保传递相关业务指标的标记训练数据。

评分是一个可选的阶段

根据你是否有标记的训练数据,你的公司可能没有评分阶段。这完全没问题,有些公司仅依靠候选生成阶段来支持它们的推荐系统产品。为内部数据工程过程建立正确的遥测以生成标记的训练数据集可能需要更多的时间和精力。

评分是一个分类问题。分类模型的输出是一个概率评分,能够让你按从最可能提升业务指标的候选项到最不可能提升的候选项进行排名。这些业务指标的示例包括点击率、视频观看时间、项目添加到购物车等。

为了服务于评分模型的输出,你可以发布一个带有模型版本的 API 端点。下游系统需要调用 API,使用基本输入特征(我们必须小心不要引入数据泄漏),然后评分模型将计算工程特征,并返回一个按概率评分排序的项目列表。

在所有 3 个公共云提供商上部署这样的端点有简单的选项:

同样,若通过批处理端点检索的排名结果可以缓存到数据库中,以便稍后由下游应用程序检索。

重新排序

在通过分配概率评分对项目进行排序后,系统还可以重新排序这些有序的项目列表。重新排序有几个好处,即:

  1. 项目质量——去除恶意项目,如虚假/盗版/诈骗产品、低质量的项目列表、点击诱饵等,这些可能会对用户信心产生负面影响,阻止他们使用平台。

  2. 项目多样性——我们可以推荐用户以前未见过的项目,或者推荐用户通常查看的不同类别的项目,以鼓励平台上的发现和探索。

当你针对业务指标进行优化时,你可能会遇到指标腐败——这会鼓励不良行为者采取某些恶意行为。重新排序阶段旨在通过考虑与评分阶段优化的主要指标互补的其他指标来减轻这一问题。

重新排名的过程可以是简单的业务规则,以确保填充项目的强制字段,或者可以训练一个独立的模型来学习恶意行为者或具有恶意意图的项目的特征。服务这些模型将类似于在评分阶段描述的方法。

为了促进平台上新项目的发现并鼓励多样性,公司在向最终用户提供排名项目列表时,也利用多臂老虎机算法的变体,以在探索与开发之间取得平衡,但这是一个需要专门博客文章讨论的主题。

# 4 特别感谢 / 参考文献

特别感谢以下资源的作者和创作者,他们帮助我写了这篇文章:

最初发表于 https://natworkeffects.com 于 2023 年 2 月 10 日。

多任务架构:综合指南

原文:towardsdatascience.com/multi-task-architectures-9bee2e080456?source=collection_archive---------13-----------------------#2023-07-18

实时多任务推理的轻量级模型

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

·

关注 发表于 Towards Data Science ·10 分钟阅读·2023 年 7 月 18 日

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

图片由Julien DuduogluUnsplash提供

介绍

你是否曾经想过如何训练一个深度神经网络来完成多项任务?这样的模型被称为多任务架构,相较于使用单独模型来处理每个任务的传统方法,它具有一定的优势。多任务架构是多任务学习的一个子集,这是一种训练模型或模型集合以同时执行多个任务的通用方法。

在这篇文章中,我们将学习如何训练一个模型同时执行分类和回归任务。本文的代码可以在GitHub找到。以下是概述:

  • 动机 — 我们为什么要这样做?

  • 方法 — 我们将如何实现这一目标?

  • 模型架构

  • 训练方法

  • 推理 — 检查性能并从一个 有趣的失败 中学习

  • 结论

动机

我们为什么要使用轻量级模型?这不会降低性能吗?如果我们不部署到边缘,难道不应该使用尽可能大的模型吗?

边缘应用需要轻量级模型以进行实时推理并减少功耗。其他应用也可以从中受益,但具体如何受益呢?轻量级模型的一个被忽视的好处是它们对计算的要求较低。通常,这可以降低服务器使用,从而减少功耗。这总体上具有降低成本减少碳排放的效果,后者可能在未来的 AI 中成为一个主要的问题

轻量级模型有助于降低成本和减少碳排放,因为它们消耗更少的电力

话虽如此,多任务架构只是工具箱中的一个工具,所有项目需求应在决定使用哪些工具之前加以考虑。现在让我们深入探讨一下如何训练其中一个模型吧!

方法

为了构建我们的多任务架构,我们将大致涵盖这篇论文中的方法,该论文中训练了一个模型以同时进行分割和深度估计。其基本目标是以快速高效的方式完成这些任务,同时可以接受性能上的损失。在多任务学习中,我们通常将相似的任务组合在一起。在训练过程中,我们还可以添加一个辅助任务,这可能有助于模型的学习,但我们可能会选择在推理过程中不使用它[1, 2]。为简化起见,我们在训练过程中不会使用任何辅助任务。

深度和分割都是密集预测任务,并且有相似之处。例如,单个物体的深度在物体的所有区域内可能是一致的,形成一个非常狭窄的分布。主要的想法是每个物体应该有其自己的深度值,我们应该能够仅通过查看深度图来识别单个物体。以同样的方式,我们应该能够通过查看分割图来识别相同的单个物体。虽然可能会有一些异常值,但我们将假设这种关系是成立的。

数据集

我们将使用City Scapes 数据集提供(左侧摄像头)输入图像分割掩码和深度图。对于分割图,我们选择使用标准训练标签,共 19 类+ 1 类未标记类别。

深度图准备—默认视差

使用SteroSGBM创建的视差图可以从 CityScapes 网站上轻松获取。视差描述了从每个立体摄像头的角度观察物体的像素差异,它与深度成反比,可以通过以下公式计算:

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

City Scapes 深度计算,单位见括号。来源:作者。

然而,默认的视差图存在噪声,许多孔对应于无限深度,以及一个总是显示自车的区域。清理这些视差图的常见方法包括:

  1. 裁剪底部 20%以及左边和顶部边缘的部分

  2. 调整到原始比例

  3. 应用平滑滤波器

  4. 执行inpainting

一旦我们清理了视差图,就可以计算深度,结果如下:

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

图 1. 来自 City Scapes 的深度数据。来源:作者。

这种方法的详细信息超出了本文的范围,但如果你感兴趣,这里有一个YouTube视频解释。

裁剪和调整步骤意味着视差(以及深度)图将不会完全与输入图像对齐。虽然我们可以对输入图像进行相同的裁剪和调整以纠正此问题,但我们选择探索一种新方法。

深度图准备—CreStereo 视差

我们探索了使用CreStereo从左右图像生成高质量视差图。CreStereo 是一个先进的模型,能够从立体图像对中预测平滑的视差图。这种方法引入了一个称为知识蒸馏的范式,其中 CreStereo 是教师网络,我们的模型将是学生网络(至少在深度估计方面)。这种方法的细节超出了本文的范围,但如果你感兴趣,这里有一个YouTube链接。

通常,CreStereo 深度图的噪声最小,因此不需要裁剪和调整。然而,分割掩码中存在的自车可能会导致泛化问题,因此我们在所有训练图像中去除了底部 20%。训练样本如下所示:

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

图 2. 训练样本。来源:作者。

现在我们有了数据,让我们来看看架构。

模型架构

根据[1],架构将包括一个 MobileNet 主干/编码器,一个LightWeight RefineNet 解码器,以及用于每个单独任务的头部。整体架构如图 3 所示。

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

图 3. 模型架构。

对于编码器/主干,我们将使用一个MobileNetV3并将 1/4、1/8、1/16 和 1/32 分辨率的跳跃连接传递到 Light Weight Refine Net。最后,输出将传递到每个负责不同任务的头部。请注意,如果需要,我们甚至可以向该架构中添加更多任务。

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

图 4.(左)详细的编码器-解码器多任务架构。(右)LightWeight RefineNet 块的详细信息。修改自

为了实现编码器,我们使用预训练的 MobileNetV3 编码器,将 MobileNetV3 编码器传递给自定义 PyTorch 模块。其前向函数的输出是一个ParameterDict的跳跃连接,用于输入到 LightWeight Refine Net。下面的代码片段展示了如何实现这一点。

class MobileNetV3Backbone(nn.Module):
    def __init__(self, backbone):
        super().__init__()
        self.backbone = backbone

    def forward(self, x):
        """ Passes input theough MobileNetV3 backbone feature extraction layers
            layers to add connections to
                - 1:  1/4 res
                - 3:  1/8 res
                - 7, 8:  1/16 res
                - 10, 11: 1/32 res
           """
        skips = nn.ParameterDict()
        for i in range(len(self.backbone) - 1):
            x = self.backbonei
            # add skip connection outputs
            if i in [1, 3, 7, 8, 10, 11]:
                skips.update({f"l{i}_out" : x})

        return skips

LightWeight RefineNet 解码器与[1]中实现的解码器非常相似,只是进行了少许修改以使其与MobileNetV3兼容,而不是 MobileNetV2。我们还注意到解码器部分包含了分割和深度头。该模型的完整代码可以在GitHub上找到。我们可以按如下方式组装模型:

from torchvision.models import mobilenet_v3_small

mobilenet = mobilenet_v3_small(weights='IMAGENET1K_V1')

encoder = MobileNetV3Backbone(mobilenet.features)
decoder = LightWeightRefineNet(num_seg_classes)
model = MultiTaskNetwork(encoder, freeze_encoder=False).to(device)

训练方法

我们将训练分为三个阶段,第一阶段为 1/4 分辨率,第二阶段为 1/2 分辨率,最后阶段为全分辨率。所有权重都会更新,因为冻结编码器权重似乎效果不佳。

转换

在每个阶段,我们执行随机裁剪调整大小、颜色抖动、随机翻转和归一化。左侧输入图像使用标准图像网均值和标准差进行归一化。

深度转换

通常深度图包含大多数较小的值,因为深度图中包含的大部分信息都是相机附近的物体和表面。由于深度图的大部分深度集中在较低的值附近(见下图 4 的左侧),因此需要对其进行变换,以便神经网络能够有效学习。深度图被裁剪在 0 到 250 之间,这是因为大距离的立体视差/深度数据通常不可靠,在这种情况下,我们希望丢弃它。然后,我们取自然对数并除以 5,以便将分布浓缩到较小范围的数字上。有关更多细节,请参见这个 笔记本

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

图 4. 左 — 裁剪后的深度分布。右 — 变换后的深度分布。深度是从 64 个随机全尺寸训练深度掩模中采样的。来源作者。

老实说,我不确定最佳的深度数据变换方法。如果有更好的方法或您会以不同的方式进行,我很想在评论中了解更多 😃.

损失函数

我们保持损失函数简单,分割使用交叉熵损失,深度估计使用均方误差。我们将它们加在一起,不加权,并联合优化。

学习率

我们使用了一个周期余弦退火学习率,最大值为 5e-4,并在 1/4 分辨率下训练 150 个周期。用于训练的笔记本位于 这里

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

图 5. 一个周期余弦退火学习率。来源作者。

我们在 1/2 分辨率下微调了 25 个周期,然后在全分辨率下再次微调了 25 个周期,学习率为 5e-6。请注意,每次我们在增加分辨率时微调时,都需要减少批量大小。

推断

在推断过程中,我们对输入图像进行了归一化,并通过模型进行了前向传播。图 6 显示了来自验证和测试数据的训练结果。

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

图 6. 推断结果(前两个是来自测试集,后两个是来自验证集)。来源作者。

通常情况下,当图像中存在较大的物体时,模型似乎能够进行分割和深度估计。当出现更精细的物体,如行人时,模型往往难以完全分割它们。模型能够在一定程度上准确估计它们的深度。

一个有趣的失败

图 6 的底部展示了一个有趣的失败案例,即未能完全分割图像左侧的灯柱。分割仅覆盖了灯柱的下半部分,而深度图显示灯柱的下半部分比上半部分更近。深度的失败可能是由于下部像素通常对应于较近的深度;注意到像素 500 附近的地平线,存在明显的分界线,将较近的像素和较远的像素区分开来。似乎这种偏差可能泄漏到了模型的分割任务中。这种任务泄漏应在训练多任务模型时考虑。

在多任务学习中,一个任务的训练数据可以影响另一个任务的表现。

深度分布

让我们检查预测的深度与真实深度的分布情况。为了简化,我们将只使用 94 对真实/预测的全分辨率深度图样本。

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

图 8. 真实(左)和预测(右)深度图分布,每个分布有 1000 个区间。来源作者。

似乎模型学习到了两个分布,一个在 4 附近有峰值,另一个在 30 附近有峰值。注意到剪切伪影似乎没有产生影响。总体分布包含了一个长尾,这表明只有图像的一小部分包含远处的深度数据。

预测的深度分布比真实值更平滑。真实值分布的粗糙度可能是由于每个物体包含相似的深度值。可能可以利用这些信息应用某种正则化来强制模型遵循这一范式,但那将是另一个问题。

额外内容:推理速度

由于这是一个旨在追求速度的轻量级模型,让我们看看它在 GPU 上的推理速度。下面的代码已从这篇文章中修改。在这个测试中,输入图像被缩小到 400x1024。

# find optimal backend for performing convolutions 
torch.backends.cudnn.benchmark = True 

# rescale to half size
rescaled_sample = Rescale(400, 1024)(sample)
rescaled_left = rescaled_sample['left'].to(DEVICE)

# INIT LOGGERS
starter, ender = torch.cuda.Event(enable_timing=True), torch.cuda.Event(enable_timing=True)
repetitions = 300
timings=np.zeros((repetitions,1))
#GPU-WARM-UP
for _ in range(10):
    _, _ = model(rescaled_left.unsqueeze(0))
# MEASURE PERFORMANCE
with torch.no_grad():
    for rep in range(repetitions):
        starter.record()
        _, _ = model(rescaled_left.unsqueeze(0))
        ender.record()
        # WAIT FOR GPU SYNC
        torch.cuda.synchronize()
        curr_time = starter.elapsed_time(ender)
        timings[rep] = curr_time

mean_syn = np.sum(timings) / repetitions
std_syn = np.std(timings)
print(mean_syn, std_syn)

推理测试表明,该模型可以以 18.69+/-0.44 毫秒或约 55Hz 的速度运行。需要注意的是,这只是一个在搭载 NVIDIA RTX 3060 GPU 的笔记本电脑上运行的 Python 原型,不同的硬件会改变推理速度。我们还应该注意,如果在 NVIDIA GPU 上部署像Torch-TensorRt这样的 SDK,可以显著提高速度。

结论

在这篇文章中,我们了解了多任务学习如何节省成本和减少碳排放。我们学习了如何构建一个轻量级的多任务架构,能够在 CityScapes 数据集上同时执行分类和回归。我们还利用了 CreStereo 和知识蒸馏来帮助我们的模型更好地预测深度图。

这个轻量级模型在速度和效率之间做出了权衡。即使有这个权衡,训练后的模型仍然能够在测试数据上预测出合理的深度和分割结果。此外,它还能够学习预测与真实深度图类似的深度分布。

参考文献

[1] Nekrasov, Vladimir 等人. ‘实时联合语义分割和深度估计使用不对称注释’. CoRR, vol. abs/1809.04766, 2018, arxiv.org/abs/1809.04766

[2] Standley, Trevor 等人. ‘在多任务学习中应该一起学习哪些任务?’ CoRR, vol. abs/1905.07553, 2019, arxiv.org/abs/1905.07553

[3] Cordts, M., Omran, M., Ramos, S., Rehfeld, T., Enzweiler, M., Benenson, R., Franke, U., Roth, S., & Schiele, B. (2016). 城市景观数据集用于语义城市场景理解. 2016 IEEE 计算机视觉与模式识别会议 (CVPR). doi.org/10.1109/cvpr.2016.350

多任务机器学习:同时解决多个问题

原文:towardsdatascience.com/multi-task-learning-4531eb32d77b

在自然语言处理和计算机视觉中,有些是监督的,有些是无监督的,有些是自监督的。

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

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

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

图片由 Gerd Altmann 提供,来源于 Pixabay

单任务学习是从标记数据集中学习预测单一结果(二元、多类别或连续)的过程。

相比之下,多任务学习是指在相同模态的输入上联合学习以预测多个结果的过程。例如图像或文本。

显而易见的问题是,为什么要联合学习?为什么不独立学习单任务模型来预测各种结果?

答案是,联合学习可以学习到在多个任务之间更好地泛化的特征。那些能够作为多个任务的良好预测器的特征会比那些不能的特征更受青睐。这些学习到的特征甚至可能在相同领域的新预测任务中进行泛化。

将无监督学习添加到其中

到目前为止,我们假设多任务设置中的所有选择任务都是监督性质的。让我们放宽这一假设,允许一些任务为无监督任务。

为什么?因为我们可能有更多的数据可以进行训练。一些是用于各种结果的标记数据,还有很多未标记数据。将无监督任务添加到联合学习中可以让我们从更大规模的数据集中进行学习。

我们实际上可以通过实证测试这个假设。我们可以保留一部分标记数据集,并进行两个实验:一个是在其余标记数据上训练,另一个是在其余标记数据和大量未标记数据的组合上训练。然后,我们可以比较这两个实验中学习到的模型在保留(标记)测试集上的质量。

事实上,从标记和未标记数据的混合中学习的概念已经有了一个名称,称为半监督学习。

将无监督学习精细化为自监督学习

虽然允许一些任务为无监督任务使我们能够使用潜在的大量未标记数据,但我们能否更好地利用未标记数据?一般的回答是肯定的。使用自监督。

在自监督中,我们定义监督任务,以从其余数据中预测数据的某些特征。例如,我们可能会预测从先前看到的单词中出现的下一个单词。

自监督比无监督学习更强大,因为它是有监督的。此外,它从所有未标记的数据中学习,因为它不需要人工标记。最后,自监督是获得大量多样化标记数据的强大机制,无需人工工作。在计算机视觉和自然语言处理部分,我们将提供简洁且现实的例子,揭示我们如何构建多样化的标记数据集,并且这种方法如何提高学习能力,相较于完全不进行自监督。

在本文中,当我们说多任务学习时,我们真正指的是至少有两个监督学习任务的学习,可能还有额外的无监督学习任务,以及可能的额外自监督任务。

我们在下面以视觉方式展示了这一点。

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

图 1:(作者提供)某种模态的数据的维恩图。实心圆表示用于各种监督任务的标记子集。虚线圆圈表示用于各种自监督任务的自标记子集。

请注意,此图仅描绘了输入数据的子集,涵盖了各种监督和自监督任务,而不是不同任务标签之间的关系。

图 1 也描绘了

  • 标记用于各种监督任务的数据子集往往较小。

  • 添加自监督任务可以覆盖更多的数据。这些子集通常可以做得更大。

  • 监督任务的子集可以相互重叠,但通常不完全相同。

放宽本文中“联合学习”一词的定义

到目前为止,我们给人的印象是联合学习意味着从所有任务的所有可用数据中同时学习一个模型。

实际上,我们希望放宽这一点以允许更灵活的训练策略。这是因为在实践中,发现首先从未标记数据中学习自监督(以及可能的无监督)任务,然后进一步调整这些模型以进行特定的下游监督任务是有效的。这种方法的一种更灵活的方式是,它自然允许在实时定义新的监督任务并根据需要微调现有模型。

关于多任务学习的数据

我们需要多个标记数据集,每个数据集用于预测一个结果。这些数据集中的输入通常来自相同领域。实际输入在数据集中可能会有所不同,因为它们通常来自不同来源。

我们可能还有未标记的数据。只要我们合理地相信这些数据会有帮助,就应该将其用于适当的无监督任务。如前一节所述,我们可以在决定之前通过实证测试我们的信念。如果适当的话,我们还应定义适合的自我监督任务,以便利用和学习从其他数据中预测未标记数据的某些特征。

多任务学习在自然语言处理和计算机视觉中尤其有用。我们将逐一讨论这两个领域。

计算机视觉中的多任务学习

想象一下,我们有手写数字、汽车、人脸图像、宠物图像等标签图像。还假设这些数据集中有些是小的。

为每个任务单独构建单任务模型的风险在于我们可能没有足够丰富的标记数据。

将所有数据集结合起来,学习一个单一的多任务模型,可能有助于缓解上述特定任务数据稀疏的问题,只要一些共同学习的特征也有助于这些任务。

思路是图像就是图像,在某些足够低的层次上,某些特征可能对多个任务的结果具有预测性,比如边缘或循环。

在这种情况下无监督任务的价值

我们之前提到,在多任务学习中加入适当的无监督任务是有益的。我们将在图像的设置中进一步阐明这一点。

网络上有数十亿张图像,大多数是未标记的。假设我们有一些特定任务的标记图像,比如预测图像中是否有建筑物。

直觉上,使用大量未标记图像进行无监督学习可以帮助发现比仅使用标记图像更好的特征,比如更好的边缘和更好的循环。

未标记数据上的自监督学习任务

如前一节所述,通常有大量的未标记图像数据。数十亿张图像。在前一节中,我们关注了半监督学习。即除了这些未标记图像外,我们还拥有一些用于特定任务的标记图像。我们希望从未标记图像中以无监督方式学习,从标记图像中以监督方式学习。

引入自监督学习可以大大提升这种方法。这是因为合适的自监督任务迫使模型学习从其他特征预测图像的一些特征。这通常可以轻松地产生大量标记数据,而不需要人工付出任何努力。

一些特定形式的自监督

以下是一些通常适用于图像数据的自监督的具体形式。

掩盖某些区域

取一张图像并模糊掉某些整个区域。创建一个新实例,其中模糊的图像是输入,原始实例是其标签。

为了激发我们对这种方法如何帮助的想象,假设我们有一些人的图像,有些人戴着帽子,有些人没有。如果我们以某种方式掩盖所有人的脸,我们可能会学到帽子下方是人的头,但反过来就不一定,即不是所有头上都有帽子。

这引出了一个问题,我们如何确定要掩盖哪些内容?我们将以不同的角度来看待这个问题。我们不尝试优化掩盖,而是建议考虑将图像分割成一定尺寸的矩形网格,并创建每个实例中掩盖一个单一网格的情况。通过选择网格的细粒度,我们可以控制从任何一个图像中获得多少被破坏的实例。

然后我们可以自动化发现哪些被破坏的图像可以被很好地重建。我们甚至可以检验这种自监督是否提高了一个或多个下游监督任务的准确性。

去色图像

通过将彩色图像变为灰色,我们可以迫使模型学习从剩余属性中尝试重建颜色。例如,它可能会学到,停牌通常是红色的,因为它们有独特的形状,甚至上面还写着Stop

降低分辨率

我们可以降低图像的分辨率,并让模型尝试重建原始图像。这可能迫使模型在可能的情况下学习填补细节。作为一个想象中的生动例子,模型可能会学会填补人脸的某些细节,比如睫毛。

NLP 中的多任务学习

考虑一个文本句子。我们可能会对标记每个词的词性、标记某些段落为命名实体以及标记某些段落为名词短语或动词短语感兴趣。

在单任务学习中,每个任务都会被独立处理。

在多任务学习中,我们会学习一个具有共享特征的模型,同时处理所有这些任务。当任务之间有协同作用时,这可以产生更好的模型。例如,学习预测句子中每个词的词性可能有助于检测命名实体、名词短语和动词短语。

在未标记数据上添加合适的任务

可能有很多句子没有标签。没有标签意味着没有词性标注,没有命名实体标注,也没有名词或动词短语标注。

我们应该考虑定义自监督学习任务,即根据目前为止看到的词来预测句子中的下一个词。这个任务可以以强大的方式利用未标记的数据。预测下一个词可以促使网络学习前面词的复杂表示。

更一般地说,定义自监督学习任务来预测句子中剩余的某些被屏蔽的词可能会很有用。我之所以说“更一般地”,是因为这些词可以出现在句子的任何地方。

这是一个例子。考虑

阳光暴露会导致皮肤癌。

我们可能考虑预测的三个被屏蔽的实例在下面两个填空场景中展示。

_____ 导致皮肤癌。

阳光暴露会导致 ______。

_ 到 _ 导致皮肤癌

在第一个场景中,我们根据其余部分预测句子的左尾。在第二个场景中,我们根据其余部分预测句子的右尾。第三个场景展示了可以有多个不连续的被屏蔽子序列需要预测。

预测被屏蔽词背后的直觉

为什么要从预测下一个词扩展到预测任何被屏蔽的词的子序列?简短的回答是:(i)这大大扩展了可用于训练的标记数据集,(ii)并且有助于在新标记的实例中呈现新类型的场景。

为了提高我们对(i)的直觉,想象一下我们为由 n 个词组成的句子的所有可能屏蔽生成被屏蔽的标记实例。屏蔽的数量大约是 2^n。相比之下,将句子切分以预测未来的词的方式大约是 n 种。

为了详细说明(ii),通过允许屏蔽出现在任何地方,我们允许模型发现潜在地比从左到右限制的关系更一般的预测关系。为了想象这一点,假设我们在语料库中有很多 X 导致 Y 的实例。同时,假设只有 X 导致 Y。如果 X 被屏蔽,模型可以从数据中学习到只有 X 导致 Y 的关系。而从左到右的语言模型不能学习到只有 X 导致 Y

第二个非常简单的例子

想象一下我们有一个汽车名称的列表。例如

本田思域,丰田凯美瑞,福特野马,吉普牧马人。

预测下一个词肯定会帮助模型学习特定品牌的模型。例如CherokeeWrangler(等)对于Jeep。然而,以屏蔽任何词的形式进行额外的自监督将帮助模型学习到品牌名称往往能够强烈预测品牌。例如,CelicaToyota 的,MustangFord 的,WranglerJeep 的,等等。

特定案例

这种类型的自监督的两个特殊案例是连续词袋模型(CBOW)和跳字模型[3]。前者对应于在某个数量的左侧和右侧上下文中掩盖中间词。后者则相反——掩盖左侧和右侧的上下文,同时保持中间的词不变。

下面是我们示例中展示的两个。

exposure to sunlight causes skin cancer.    # Original
exposure to sunlight _ skin cancer.         # Masking for CBOW
_ _ _ causes _ _                            # Masking for Skip-gram.

另一个被发现有用的自监督任务是下句预测[2]。特别是对于问答系统。在[2]中,这个任务被表述为一个二分类问题。输入(X1,X2)被解释为“句子 X1 后面跟随句子 X2”。正实例是通过语料库中相邻句子的对生成的。负实例是通过将语料库中的一个句子 X1 与语料库中的随机句子 X2 配对生成的。

在上述段落的背景下,“句子”一词指的是任何连续的文本序列[2]。因此,从当前段落预测下一个段落也会被视为下句预测问题。

利用深度架构

多任务学习可以显著利用深度架构。直觉是这样的。深度架构在靠近输入的层中学习低级特征,而在靠近输出的层中学习高级特征。直觉上认为,学习低级特征的较深层次恰恰是那些可以在任务之间共享的层。

考虑我们的多任务图像分类场景。可以合理推测,像边缘或循环这样的低级特征将在这些任务中的多个任务中成为有用的预测因子。

下面展示了一个深度架构,其中输入层靠近的层由所有监督任务共享,而靠近任务的层则是任务特定的。

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

图 2:一个具有共享层的多任务深度架构。(作者提供。)

训练可以通过常规的反向传播算法进行。训练集中的特定实例可能只有一个任务的标签。这个任务的预测与目标之间的误差通过任务特定层反向传播,然后传递到共享层。因此,如果我们混入来自不同任务的实例,共享层会学习到在任务间通用的表示。

总结

在这篇文章中,我们涵盖了多任务学习的话题。我们解释了为什么多任务学习可以产生理解和泛化能力比单独学习每个任务的模型更好的模型。当我们将无监督和自监督任务加入混合时,这种方法尤为强大。这些任务使我们能够从未标记的数据中学习很多。

我们还涵盖了计算机视觉和自然语言处理中的多任务学习。在这些设置中,我们讨论了具体的监督任务、可用数据的性质以及具体的自监督方法。

进一步阅读

  1. ai.facebook.com/blog/self-supervised-learning-the-dark-matter-of-intelligence/

  2. arxiv.org/pdf/1810.04805.pdf

  3. arxiv.org/pdf/1301.3781.pdf

  4. arxiv.org/pdf/2103.01988.pdf?fbclid=IwAR2pqhYda6MV9r2b3Afx_0eKUiZhX-Es6Pa_FbLOqH8fglQzO2kY3yKxZE8

推荐系统中的多任务学习:基础知识

原文:towardsdatascience.com/multi-task-learning-in-recommender-systems-a-primer-508e661a2029

试图做到这一切的算法背后的科学和工程

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

·发布于 Towards Data Science ·阅读时长 8 分钟·2023 年 7 月 25 日

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

Mike KononovUnsplash 的照片

尽管多任务学习在计算机视觉和自然语言处理领域已经得到很好的应用,但在现代 推荐系统 中的使用仍然相对较新,因此理解还不够充分。

在这篇文章中,我们将深入探讨多任务推荐系统中的一些重要设计考虑因素和最新研究突破。我们将涵盖

  • 为什么我们首先需要多任务推荐系统,

  • 正向迁移和负向迁移:多任务学习者的关键挑战,

  • 硬参数共享和专家建模,以及

  • 辅助学习:为了提高主任务的性能而添加新任务的想法。

让我们开始吧。

为什么要使用多任务推荐系统?

多任务推荐系统的关键优势在于其能够同时解决多个业务目标。例如,在视频推荐系统中,我们可能希望优化点击量,但同时也要优化观看时长、点赞、分享、评论或其他形式的用户互动。在这种情况下,单一的多任务模型不仅比多个单任务模型在计算上更便宜,而且每个任务的预测准确性也可能更高。

即使在我们只想预测一个事件的情况下,比如电子商务推荐系统中的“购买”行为,我们仍然可以添加额外的任务,其唯一目的是提高主要任务的性能。我们称这些额外的任务为“辅助任务”,这种学习形式为“辅助学习”。在电子商务的例子中,除了“购买”之外,还可以学习“加入购物车”和“添加到列表”,因为这些事件彼此紧密相关:它们表示购物意图。

哪些任务可以很好地一起学习?

从高层次来看,预测第二个任务要么有助于第一个任务,要么产生相反的效果:使第一个任务的预测变得更差。我们称前者为“正向迁移”,后者为“负向迁移”。多任务学习的挑战在于仅学习那些具有正向迁移的任务,并避免负向迁移,因为后者可能会对模型性能产生不利影响。

多任务学习中的一个关键问题是哪些任务可以很好地一起学习。在许多情况下,我们可以凭借领域知识做出合理的猜测。我们在上面已经看到一个例子:“购买”和“加入购物车”都表示购物意图,因此应该在多任务学习者中表现良好(实际上,它们确实如此)。

然而,如果任务数量变得很大,我们可能需要算法上确定哪些任务应该一起学习,哪些应该分开学习。值得注意的是,这是一个 NP 难题,因为可能的任务分组数量随着任务数量的增加而呈指数级增长。这并不容易,但可以做到:在 2020 年的一篇 论文 中,斯坦福大学的作者使用“分支限界”算法解决了计算机视觉数据集上的任务分组问题,得出的解决方案优于多个单任务学习者和单一的多任务学习者。

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

3 种不同的多任务建模范式。图源自 Ma et al 2018

硬参数共享:多任务学习的幕后

构建多任务神经网络的最简单方法是一个被称为“硬参数共享”或“共享底部”的技术,在这种方法中,我们将一个共享底部模块与任务特定的顶部模块结合在一起。通过这种方式,底部模块可以学习任务通用的模式,而顶部模块则可以学习任务特定的模式。(“模块”在这里指的是具有特定激活函数的多层感知机(MLP))。

在最简单的情况下,任务特定模块可以是一个单一的输出神经元,用于进行预测。然而,在实践中,我们通常可以通过为每个任务添加一个专用模块来实现更好的性能,该模块可以学习数据的任务特定内部表示。

多任务学习者的输出将是一个预测列表,我们可以将其合并为最终损失,如

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

其中 p 是预测值,y 是标签,w 是(可选的)任务特定权重,控制每个任务的相对重要性。

专家建模

硬参数共享也许是解决多任务学习问题最常见且最简单的方法,但它有一个主要缺点:我们必须提前决定网络的哪些部分应该共享,哪些不应该。这要求我们知道哪些任务能很好地一起学习,哪些不能,但实际上,我们可能无法提前获得这些信息。

进入“专家建模”,也称为“专家混合”(MoE),追溯到 1991 年一些 AI 大咖,如 Robert Jacobs、Michael Jordan、Steven Nolan 和 Geoffrey Hinton 的论文。MoE 的关键思想是通过门控网络结合 N 个专家,根据输入数据选择最佳专家。在这里,

  • “专家”是一个处理数据的 MLP,结果是一个嵌入或预测,并且

  • “门控网络”只是一个 softmax 函数:

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

其中 x 是输入数据,W 是一个可学习的矩阵。换句话说,W(因此门控)学习根据输入数据选择正确的专家。

然而,MoE 只使用一个门控,这在有多个任务的情况下可能效果不好,每个任务都需要自己的专家集。因此,多任务推荐系统的一个突破是用多个门控替代 MoE 中的单一门控,每个任务一个,结果就是“MMoE”,即“多门控专家混合”,在 2018 年由 Google 提出的论文中介绍。

作者展示了 MMoE 在合成数据、普查数据以及大规模生产推荐数据集上均优于硬参数共享和 MoE。MMoE 特别适用于(相比于 MoE)任务相关性较低的情况,突显了拥有多个门控的优势。

辅助学习

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

MetaBalance 的关键思想是将辅助梯度缩放到与主要任务的梯度相匹配,如从左到右的图表过渡所示。图源自He et al 2022

在许多推荐问题中,通过联合学习辅助任务可以改善主要任务的预测性能。例如,

  • 在预测转化率时,当联合学习预测点击率的辅助任务时,预测性能会提高(Ma et al 2018),

  • 当尝试预测用户评分时,预测性能在联合学习辅助任务预测项目元数据(如类别和标签)时会得到改善(班萨尔等 2016)。

  • 当尝试预测新闻推送中的阅读时长时,预测性能会在联合学习辅助任务预测点击率时得到改善(赵等 2021),仅举几个例子。在所有这些情况下,辅助任务的目的是不是在推理时使用预测,而仅仅是提升我们试图学习的主要任务的预测性能。

辅助学习之所以有效,是因为我们添加了梯度信号,这有助于模型在参数空间中找到最佳潜在最小值,而当主任务的梯度信号稀疏时,这种额外的信号尤为有用。例如,转换率远低于点击率,因此预计后者的梯度信号更丰富,可以补充前者。

然而,已显示辅助学习中的梯度可能高度不平衡,以至于辅助梯度要么主导学习,要么根本无关紧要。这是一个问题,假设“MetaBalance”的作者,2022 年提出的高级辅助学习算法论文来自 Meta。MetaBalance 的关键思想是将辅助梯度缩放到与主要任务梯度相同的数量级。

形式化地说:

g_aux <-- g_aux * r * |g_main|/|g_aux|,

其中 g_aux 是来自辅助任务的梯度,g_main 是来自主要任务的梯度,r 是作者通过经验确定的超参数。论文中 r~0.7 似乎效果最佳:换句话说,当辅助任务的梯度与主要任务的梯度接近但不完全相同时,辅助任务帮助最大。

事实上,MetaBalance 展示了令人鼓舞的结果。作者考虑了两个电子商务购物数据集,其中目标是“购买”,辅助任务包括“点击”、“添加到购物车”和“添加到列表”。相较于单任务和普通(共享底层)多任务建模,MetaBalance 的改进是显著的。在一个问题中,他们将 NDGC@10 从 0.82(普通多任务)提高到 0.99(MetaBalance),提升了 17%!

总结

回顾一下:

  • 多任务学习很重要,因为现代推荐系统通常需要同时优化多个业务目标。

  • 不是所有任务都能很好地一起学习。任务可以互相帮助,产生正迁移,也可以相反——彼此对抗——产生负迁移。确定哪些任务可以一起学习是一个 NP-hard 问题!

  • 硬参数共享(即“共享底层”)是解决多任务学习最简单和最常见的方法。这是建立稳固基线时应该首先尝试的方法。

  • 专家建模,特别是 MMoE,是当前解决多任务学习问题的最先进技术,同时能够减轻负迁移。

  • 在辅助学习中,我们增加了额外的任务,其唯一目的是提高主要任务的性能。辅助学习在多个应用场景中已被证明能带来显著的建模改进,并且通过扩展辅助梯度可以进一步提升效果。

而这仅仅是冰山一角。在这个领域还有很多未解之谜:如何设计良好的辅助任务?辅助任务与主要任务的最佳比例是多少?最佳的专家数量是多少?它如何随着任务数量的增加而扩展?专家模块应该有多大?

请继续关注这个领域。新的突破无疑就在前方。

对现代推荐系统仍感到好奇?请继续阅读:

多层次回归模型与辛普森悖论

原文:towardsdatascience.com/multilevel-regression-models-and-simpsons-paradox-acb9820e836d?source=collection_archive---------2-----------------------#2023-08-08

使用适当的工具避免错误结论

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

·

关注 发布于 Towards Data Science ·10 分钟阅读·2023 年 8 月 8 日

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

数据分析会影响我们的结论,但我们应使用适当的工具来走上正确的道路。照片由Brendan Church拍摄,发布在Unsplash

数据分析——顾名思义——是数据科学家工作的重要组成部分,从描述性统计和简单的回归模型到复杂的机器学习方法。然而,这些方法需要小心处理,选择正确的方法远非简单。复杂的数据通常包含隐藏的结构,如果未得到适当考虑,可能会导致谬误,最终得出无效的结论。

在这篇文章中,我想举一个 辛普森悖论 的例子,并展示如何通过简单但目光短浅的分析导致看似有数据支持的错误结论,尽管这些结论不过是误解。通过这种方式,我演示了 多层回归模型 作为分析层次结构数据(即嵌套数据)的适当方法。

问题

我们现在就开始吧!假设我们已经发布了一个智能手机应用,并且想了解更多关于我们的用户及其满意度的信息。因此,我们进行了一项小调查,询问一些用户在 1(非常不满意)到 4(非常满意)的范围内对我们应用的满意度评分。此外,我们还测量了他们在过去一周内在应用中花费的时间,为了获取丰富的样本,我们询问了不同国家的用户。然后我们的数据可能会像这样(本文使用了生成的数据):

 Satisfaction  Time_spent  Country
0      2.140440    1.585295        0
1      2.053545    0.636235        0
2      1.589258    1.468033        1
3      1.853545    0.968651        2
4      1.449286    0.967104        2
.      .            .              .
.      .            .              .
.      .            .              .

我们对我们应用中花费时间与报告的满意度之间的关系感兴趣。准确地说,我们想知道在我们的应用中花费更多时间是否与更高或更低的满意度相关,并且我们想量化这种关联,即我们想做出类似于“在我们的应用中多花一小时与满意度提高 x 倍/降低 x 倍相关”的陈述。当我们查看数据时,我们可能已经有了第一直觉,即在应用中花费更多时间与较低的满意度相关:

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

线性回归

让我们做一个线性回归来验证我们的猜测。通过线性回归,我们尝试预测满意度,前提是时间花费作为形式为 satisfaction = intercept + regression_coefficient * time_spent 的线性函数。我们可以使用 statsmodels 包中的 OLS(普通最小二乘法)函数轻松完成这一点。

import statsmodels.api as sm
result = sm.OLS(df["Satisfaction"], sm.add_constant(df["Time_spent"])).fit()
print(result.params)

add_constant 方法只是我们用来告诉模型我们希望在方程中有一个截距的技术细节(这在数据未标准化时是必需的)。result.params 给出了两个值,即 interceptconst)和 regression_coefficient 对应的 Time_spent 变量。

const         3.229412
Time_spent   -0.655470

也就是说,我们的模型告诉我们,满意度可以预测为3.229 –0.655time_spent*。换句话说,在应用程序中多花一小时的时间会导致满意度下降 0.655 分(由于负号)。然而,满意度并非从零开始,而是一个人从第一印象(即time_spent=0)开始的平均满意度为 3.229。我们还可以将其以截距为 3.229 和斜率为-0.665 的线表示出来:

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

当然,这个预测并不完美,但至少它给出了一个趋势。好的,情况已经清楚了,对吧?在应用程序中花费更多时间会导致满意度下降,我们甚至可以量化这种下降。我们现在可以从中得出结论,并考虑如何改进应用程序(当然,我们希望用户在使用应用程序时更满意),或者进行更详细的调查,以找出用户不满意的原因。

慢一点!

按国家分组

记得我们从不同国家的用户那里收集了数据吗?如果我们分国家查看这些数据会发生什么呢?在下面的图表中,我们看到的是之前相同的数据点,但现在我们将每个国家用不同的颜色突出显示。

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

从这个图表中我们可以观察到两点。首先,各国在满意度和在应用程序中花费的时间上似乎有所不同。来自蓝色国家的受访者在应用程序中花费的时间更多,但相比其他国家的受访者,他们的满意度更低。更进一步,当我们将三个国家分开来看时,我们可能会认为应用程序使用时间与满意度之间的关系确实是正向的。这不是与我们之前的分析相矛盾吗?

辛普森悖论

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

实际上,这个名字并不是以那些辛普森家族命名的……照片由Stefan Grage提供,来源于Unsplash

我们刚刚看到的效果被称为辛普森悖论。当数据中的相关性在组之间与组内不同的时候,就会出现这种情况。尽管这一点非常违反直觉,但确实可能发生(正如我们刚刚看到的),其原因在于混杂变量。让我们用上述例子来解释。当孤立地看每个国家时,我们会看到一个正趋势:在应用程序中花费更多的时间与更高的满意度相关。然而,正如我们已经看到的,各国的平均满意度和应用程序使用时间不同。在蓝色国家,平均满意度较低,但在应用程序中花费的时间比橙色或绿色国家要长;这种趋势与国家内部的趋势相反。然而,可能还有其他变量导致这种情况。例如,可以想象,在蓝色国家,更多的人经常感到无聊,导致总体满意度较低(因此对我们的应用程序的态度较为消极),但在应用程序中花费更多时间。当然,这只是一个可能的解释,可能还有许多其他解释。然而,目前正确的解释并不是特别重要。对我们来说,重要的是理解不同国家之间存在系统性的差异。

那么,我们为什么在之前的分析中没有发现这一点呢?我们在进行线性回归时是否犯了错误?确实,因为完全进行线性回归是不对的,因为线性回归的一个核心假设被违反了:线性回归假设所有数据点是独立采样的,并且来自相同的分布。然而,在我们的例子中并非如此!显然,不同国家之间的应用程序使用时间和满意度的分布是不同的。现在,如果线性回归的假设被违反,线性回归就不是数据分析的合适工具。

层次模型

我们现在可以做什么,以便以更合适的方式分析我们的数据?幸运的是,有统计模型可以将线性回归的理念扩展到层次数据。我们说数据是层次数据,是指我们采样的数据点嵌套在一个层次结构中,就像在我们的案例中,受访者被嵌套在各个国家中。这些统计模型被称为层次线性模型多级模型线性混合效应模型。这些模型通过引入所谓的固定效应随机效应来考虑组结构。在一个简单的例子中,当我们想根据一个变量预测另一个变量时(比如我们想预测在应用程序中花费的时间与满意度的关系),固定效应包括一个截距和所有组共同的一个斜率。这与线性回归中的情况完全一样。

现在,随机效应可以引入每组单独的截距偏差。例如,蓝色国家的截距可能比固定截距略低,而绿色国家的截距可能比固定截距略高。这将解释国家间满意度均值水平的差异。

此外,随机效应可以引入每组的斜率偏差。例如,在橙色组中,斜率可能高于固定斜率(即,满意度与时间花费之间的关联更强),而在绿色国家中,斜率可能较低。

层次模型的实际效果

让我们通过实际操作来理解发生了什么。我们进行了一项新的分析,但这次我们使用了 statsmodels 的mixedlm函数。我们明确表示希望根据时间花费来预测满意度(而不是相反),使用公式*“满意度 ~ 时间花费”,并指出数据框中的“国家”列用于确定不同的组。此外,参数re_formula="时间花费”*告知模型我们希望每个组有一个单独的斜率。如果没有这一点,随机效应只会考虑组特定的截距,而不会考虑组特定的斜率。

import statsmodels.formula.api as smf

result = smf.mixedlm("Satisfaction ~ Time_spent", data=df, groups=df["Country"], re_formula="Time_spent").fit()
print(result.fe_params)
print(result.random_effects)

如果我们打印出固定效应fe_params)和随机效应,我们会得到如下值:

 Fixed effects
  Intercept     2.286638
  Time_spent    0.497657
Random Effects
  {0: Group -0.958805, Time_spent -0.018178,
   1: Group 0.155233,  Time_spent 0.274222,
   2: Group 0.803572,  Time_spent -0.256044}

那么,这意味着什么?对于固定效应,我们有一个截距值和一个变量时间花费的值。然而,对于随机效应,我们有每个国家(0,1,2)的两个值:一个是截距值(),另一个是我们变量的斜率值(时间花费)。正如我们上面看到的,随机效应描述了每组的偏差。对于我们的三个组,我们可以通过将随机效应加到固定效应的截距和斜率中来构造三个不同的线性方程。

satisfaction_0 = (2.286638 - 0.958805) + (0.497657 - 0.018178) * time_spent = 1.327833 + 0.479479 * time_spent
satisfaction_1 = (2.286638 + 0.155233) + (0.497657 + 0.274222) * time_spent = 2.441871 + 0.771879 * time_spent
satisfaction_2 = (2.286638 + 0.803572) + (0.497657 - 0.256044) * time_spent = 3.090210 + 0.241613 * time_spent

我们看到组 0 的随机截距为负值(-0.958),组 2 的随机截距为正值(0.803),所以组 0 低于固定截距,而组 2 高于固定截距。因此,组 0 在其线性函数中的截距最低(1.327),而组 2 最高(3.090)。换句话说,在国家 0 中,满意度的起始水平低于国家 2。

我们还看到组间的斜率存在差异。在组 1 中,斜率最高为 0.771,而在组 2 中仅为 0.241。这意味着在应用中的满意度与时间花费之间的关联在国家 1 中远高于国家 2。换句话说,在国家 1 中,每增加一个小时的应用时间,满意度会增加 0.771 分(均值),而在国家 2 中仅增加 0.241 分。此外,所有斜率都是正值,这与我们从上图预期的一致,但与我们一开始进行的线性回归的负斜率相矛盾。

现在我们可以为每个国家绘制一条回归线:

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

现在我们清楚地看到每个国家的积极趋势和不同的截距(即,当time_spent=0 时,线条的位置)。

结论

在上述示例中,我们看到短视的分析很容易导致虚假的结论。忽视数据的嵌套结构,即来自不同国家的用户,我们本可以在进行线性回归后就停止,得出更多时间花费在应用中与较低的满意度相关的结论。只有通过理解我们的数据不符合线性回归的核心假设,因为数据点不是从同一分布中抽取的,我们才有动机进行进一步分析,揭示了实际情况正好相反:更多时间花费在应用中确实与更高的满意度相关。

那么,让我们从这个例子中总结一些要点:

  • 在使用统计方法进行分析之前,应该验证其假设是否符合数据。

  • 对于嵌套数据,假设所有数据点都来自同一分布的情况可能并不总是成立。

  • 可能发生的情况是,整体数据的趋势与构成该数据的单个组内部的趋势不同。这被称为辛普森悖论。

  • 多级线性模型是应对嵌套数据结构并避免因辛普森悖论产生虚假结论的一种方法。

进一步阅读

我们使用了在statsmodels中实现层次模型的以下方法:

我使用了以下统计学教材(遗憾的是,这本书仅有德文版)。

  • Eid, M., Gollwitzer, M., & Schmitt, M. (2017). 统计与研究方法

关于多级模型的背景信息也可以在这里找到:

如果你想重现结果,数据是这样生成的:

import numpy as np
import pandas as pd
group_1_x = np.random.uniform(0.5, 1.8, 25)
group_1_y = (1 + 0.3 * group_1_x) + np.random.rand(len(group_1_x))

#start_2, end_2, step_2 = 0.3, 1.3, 0.04
group_2_x = np.random.uniform(0.3, 1.3, 22)
group_2_y = (2 + 0.7*group_2_x) + np.random.rand(len(group_2_x))

#start_3, end_3, step_3 = 0, 1, 0.04
group_3_x = np.random.uniform(0, 1, 32)
group_3_y = (2.5 + 0.3*group_3_x) + np.random.rand(len(group_3_x))

all_x = np.concatenate([group_1_x, group_2_x, group_3_x])
all_y = np.concatenate([group_1_y, group_2_y, group_3_y])
df = pd.DataFrame({"Satisfaction": all_y, "Time_spent":all_x, "Country":[0]*len(group_1_x) + [1]*len(group_2_x) + [2]*len(group_3_x)})

喜欢这篇文章吗? 关注我 以便接收我未来的帖子。

用 R 进行的多层回归

原文:towardsdatascience.com/multilevel-regression-with-r-eb3eb7d8de88

通过这个简单的解释和示例来理解层级线性模型

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

·发布在 Towards Data Science ·阅读时间 9 分钟·2023 年 5 月 15 日

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

图片由 Lidya Nada 提供,来源于 Unsplash

介绍

回归模型已经存在很长时间了,远早于机器学习的出现。统计学家们早在 1900 年代之前就开始使用这些模型来理解变量之间的关系,当时 Sir Francis Galton(1885)提出了这一思想。

幸运的是,自那时起,理论、计算机和技术都有了很大的发展,以至于我们可以说,现如今创建这样的模型是简单的(如果不是最简单的话)。

但不要被它的实现简单性所迷惑。它确实很简单,但也不是那么简单。有不止一种回归模型可用,不仅仅是普通最小二乘法(OLS)。

我们在这篇文章中讨论的是层级线性模型(HLM),或者简单地说,就是多层回归

准备工作

在深入内容之前,让我们先了解一下本例中将使用的数据集以及我们在编码时需要的库。

加载以下库。如果你没有安装其中任何一个,只需使用 install.packages("library_name") 进行安装。

library(nlme)
library(tidyverse)
library(ggridges)

数据集汽油在许多 R 库中均可用。在这个例子中,它是通过 R 的 nlme 库访问的,许可证为 GPL 2.0。它最初来源于 Prater, N. H. (1955), Estimate gasoline yields from crudes, Petroleum Refiner, 35

这个数据框包含以下变量:

  • **yield**:原油转化为汽油的百分比

  • **endpoint**:所有汽油蒸发的温度(华氏度)

  • **Sample**:原油样本编号

  • **API**:原油重力(API 度数)

  • **vapor**:原油的蒸汽压(lbf/in2)

  • **ASTM**:10%的原油变成蒸汽的温度。

# Dataset
data("Gasoline")

目标:我们的目标是预测*yield*数值,即从一个样本中转化为汽油的原油量。

关于 OLS 的小回顾

在 OLS 回归模型中,我们取一个或多个解释变量***[X],并尝试使用这些数字来解释由响应变量[y]***测量的主题行为。

对于 OLS,最佳的特征选择统计测试是相关性。毕竟,最常用的衡量线如何拟合数据的指标是 R²,这只是相关性平方(附注:确实还有其他模型性能指标,应该与 R²一起使用)。

因此,我们的目标是绘制一条最佳拟合数据的线。换句话说,我们希望这条线与数据点一致,绘制出响应变量的平均值。此外,我们希望这条线与实际数据点之间的差异尽可能小。

更技术一点,响应变量将由一个方程确定,我们希望优化 y 轴上的截距点alpha和这条线的斜率,而斜率由beta系数决定,正如下文所述。

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

简单线性回归方程。作者提供的图片。

如果我们查看我们的数据集,这就是 OLS 模型将会做的事情:

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

从汽油数据集创建的 OLS 回归模型。作者提供的图片。

这是一个不错的拟合,但不是最佳模型。它可以在某种程度上解释yield的方差,但我们可以做得更好,如下所示。

层次线性模型 [HLM]

HLM 与传统 OLS 模型的不同之处在于它考虑了数据的自然分组。

多层回归就像为数据中的每个组创建一个不同的回归模型。

从我们的数据来看,首先引起我注意的是存在不同的Sample。所以,这引出了问题:

  • 不同样本会影响产量的估计吗?

  • 如果我们通过样本添加多个回归层次,结果会更好吗?

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

汽油数据集摘录。作者提供的图片。

组件

HLM 模型由固定效应和随机效应组件组成。固定效应可以估计 X 变量和 y 之间的关系,而随机效应组件将为每个组确定不同的截距和斜率系数,帮助创建对每个组更合适的估计。

请看下面的多层回归模型的样子。

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

多层次模型 HLM2,一级=观察,二级=样本。图像由作者提供。

实际上,会创建一个回归模型来计算固定组件,这对于每个组的每个观察值都是 y = a + bx。然后,顺序地计算随机组件,这是对更多或更少的调整,根据该组的最佳截距和/或斜率。

HLM 模型可以有随机组件,仅改变截距、仅改变斜率或两者都有。在前面的图中,随机组件是截距和斜率,因为每个样本的回归线不平行且有不同的截距点。

编码

现在让我们创建一个 OLS 模型和一个 2 级的 HLM,然后比较结果。

我们可以先进行相关性测试,以了解回归中使用的最佳变量。我们会发现 endpoint 是与 yield 相关性最高的变量。

# Check for the best correlations
cor(Gasoline[,-3])

              yield   endpoint        API      vapor       ASTM
yield     1.0000000  0.7115262  0.2463260  0.3840706 -0.3150243
endpoint  0.7115262  1.0000000 -0.3216782 -0.2979843  0.4122466
API       0.2463260 -0.3216782  1.0000000  0.6205867 -0.7001539
vapor     0.3840706 -0.2979843  0.6205867  1.0000000 -0.9062248
ASTM     -0.3150243  0.4122466 -0.7001539 -0.9062248  1.0000000

在 R 中创建 OLS 模型非常简单。原生函数 lm() 可以轻松处理。让我们看看。

# OLS model
model_ols <- lm(yield ~ endpoint, data=Gasoline)
summary(model_ols)

[OUT]
Call:
lm(formula = yield ~ endpoint, data = Gasoline)

Residuals:
     Min       1Q   Median       3Q      Max 
-14.7584  -6.2783   0.0525   5.1624  17.8481 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -16.66206    6.68721  -2.492   0.0185 *  
endpoint      0.10937    0.01972   5.546 4.98e-06 ***
---
Signif. codes:  0***0.001**0.01*0.05.0.1 ‘ ’ 1

Residual standard error: 7.659 on 30 degrees of freedom
Multiple R-squared:  0.5063, Adjusted R-squared:  0.4898 
F-statistic: 30.76 on 1 and 30 DF,  p-value: 4.983e-06

# Calculate Log Likelihood
logLik(model_ols)
'log Lik.' -109.5206 (df=3)

结果:模型在统计上显著(F 检验的 p 值<0.05),系数也显著。

  • R²: 50%

  • Loglik: -109.52

  • MAE: 5.95(yield 平均值 = 19.66)

  • RMSE: 7.41(标准差:10.72)

我们的主要绝对误差约为 y 平均值的 30%。这太多了。正如我们在单一回归线的图中看到的那样,这条线位置良好,但无法完全捕捉方差,因为有许多点离平均值太远。

现在让我们尝试运行一个 HLM。

# Multilevel Model with fixed slope and random intercept
model_multi <- lme(fixed = yield ~ endpoint,
                       random = ~ 1 | Sample,
                       data = Gasoline,
                       method = "REML")

[OUT]
Linear mixed-effects model fit by REML
  Data: Gasoline 
       AIC      BIC   logLik
  183.4306 189.0354 -87.7153

Random effects:
 Formula: ~1 | Sample
        (Intercept) Residual
StdDev:    8.387862  1.88046

Fixed effects:  yield ~ endpoint 
                Value Std.Error DF   t-value p-value
(Intercept) -33.30626  3.287182 21 -10.13216       0
endpoint      0.15757  0.005703 21  27.62678       0
 Correlation: 
         (Intr)
endpoint -0.582

Standardized Within-Group Residuals:
       Min         Q1        Med         Q3        Max 
-1.6759908 -0.4857902 -0.0397993  0.5128182  1.8518205 

Number of Observations: 32
Number of Groups: 10 

这是代码中发生的情况。我们使用 nlme 库中的 lme 函数运行了一个多层次模型。固定组件是回归 yield ~ endpoint随机组件仅针对截距。注意 random = ~1 | Sample~1 意味着固定斜率——即按组的平行回归线——和按 Sample 计算的随机截距。

与简单的 y = a + bx 相比,这个模型变成了:

  • 固定: [截距 + b*endpoint] +

  • 随机: [ 由样本随机效应 + 错误]

与 OLS 模型相比,注意它如何改进了。

  • Loglik: -87.7153(数字越高越好)。

  • MAE: 1.16

  • RMSE: 1.52

哇,已经改进了很多:LogLik 测量值提高了大约 20%。

我们还可以进一步改进吗? 我们可以尝试向模型中添加另一个变量。我们将选择 vapor,因为它与响应变量的相关性是下一个最强的。

这次,我们将运行一个带有随机斜率和截距的 HLM,这意味着我们将允许模型在交叉 y 轴的位置和拟合线的倾斜度上变化,而不是将其静态为平行线。

在下面的代码片段中,我们正在计算yieldendpointvapor函数中的一个固定部分(按观察、按样本)。随机部分将根据endpoint值计算回归的斜率,并根据Sample计算截距。下一个模型将是:

  • 固定:[截距 + (b1 * 终点) + (b2 * 蒸汽)] +

  • 随机:[截距随机效应由样本 + 斜率随机效应 * 终点 + 误差]

# Multilevel Model with random slope and intercept
model_multi2 <- lme(fixed = yield ~ endpoint + vapor,
                   random = ~ endpoint | Sample,
                   data = Gasoline,
                   method = "REML")

# Look how the fitted values are presented
# It is divided in Fixed and the result with the levels
# "Sample" is the final prediction, with the random effects considered
model_multi2$fitted

        fixed     Sample
1   9.9034036  6.2878876
2  17.4490277 16.9089759
3   6.2864457  8.2047743
4  13.2051879 10.0773886
5  -0.3240537  6.1951732

为了公平起见,我还使用了蒸汽变量运行了另一个模型。接下来,我们将比较所有模型。

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

比较创建的 4 个模型。图像由作者提供。

尽管 OLS 模型endpoint+vapor给出了一个有趣的 LogLik 值,但我们还是更仔细地看看预测,以了解哪个模型更合适。

# Results table
results <- data.frame(
   yield = Gasoline$yield,
   pred_ols = model_ols$fitted.values,
   pred_ols2 = model_ols2$fitted.values,
   pred_multi = model_multi$fitted[,'Sample'],
   pred_multi2 = model_multi2$fitted[,'Sample']

   yield  pred_ols  pred_ols2 pred_multi pred_multi2
1    6.9  9.040133 11.2681120  5.8234477   6.2878876
2   14.4 16.914846 17.8195910 17.5516334  16.9089759
3    7.4  6.524599  8.0634010  8.6490752   8.2047743
4    8.5 23.258365 13.5848551  9.9282663  10.0773886
5    8.0  7.180825  1.9380928  6.1502045   6.1951732
6    2.8  9.040133 -0.2448399 -0.2263362   0.6677289
7    5.0 14.508684  5.1154648  5.7778750   5.0504530
8   12.2  5.759002 13.7816308 12.3278463  11.9321042
9   10.0 12.540005 13.3171528 10.1678972  10.3006218
10  15.2 16.149249 20.3249040 16.0654477  16.3795097
11  26.8 23.477107 26.1797067 27.0057873  27.2203084
12  14.0 21.727171 17.5245089 13.0730720  13.5617889
13  14.7 24.789559 15.5355488 12.1342356  12.1958142
14   6.4 13.414973  5.3285706  6.0764330   6.4725490
15  17.6 23.258365 16.2622858 18.3834134  18.1474338
16  22.3 13.414973 23.5350992 23.3576925  23.3378940

OLS 模型 2 做得非常好,但多层回归更加一致。例如,查看第 4、5、9、10、12 行的拟合值,两个多层模型将提供更好的估计。然而,尽管存在这些误差,OLS2 对这些数据来说并不是一个糟糕的模型。

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

比较创建的 4 个模型。图像由作者提供。

这个最终的视觉效果可以给我们一个模型的比较视图。毫无疑问,最佳拟合模型是右下角的最后一个 HLM 模型。

离开前

在本教程中,我想为你介绍层次线性模型或多层回归模型。当你有数据自然嵌套在不同组中时,这些模型会非常有用,并且这些组会影响估计的方式。

使用多层模型,你可以允许回归线根据组变化其斜率和/或截距。

在 R 中进行 HLM 建模所用的包是nlme

这是这个练习的代码,在 GitHub 上。

[## Studying/R/Multilevel Regression at master · gurezende/Studying

你目前无法执行该操作。你在另一个标签或窗口中登录。你在另一个标签或窗口中注销了……

github.com](https://github.com/gurezende/Studying/tree/master/R/Multilevel%20Regression?source=post_page-----eb3eb7d8de88--------------------------------)

如果你喜欢这个内容,不要忘记关注我。

[## Gustavo Santos - Medium

阅读 Gustavo Santos 在 Medium 上的文章。数据科学家。我从数据中提取见解,以帮助人们和公司……

gustavorsantos.medium.com](https://gustavorsantos.medium.com/?source=post_page-----eb3eb7d8de88--------------------------------)

此外,如果你考虑加入 Medium 成为会员,以便访问我的文章以及其他成千上万的优质内容,这是我的推荐码

你可以在LinkedIn找到我,也可以通过TopMate 预订与我进行快速聊天

参考文献

Fávero, L. & Belfiore, P. 2022. Manual de Análise de Dados. 第 1 版. LTC,里约热内卢,巴西。

使用 Azure ML 和 MONAI 的多模态 3D 脑肿瘤分割

原文:towardsdatascience.com/multimodal-3d-brain-tumor-segmentation-with-azure-ml-and-monai-4a721e42f5f7?source=collection_archive---------6-----------------------#2023-03-21

在企业级 ML 平台上大规模运行医学影像框架

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

·

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

作者:Harmke AlkemadeAndreas Kopp

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

3D 脑肿瘤分割(图片来源于 Shutterstock,授权给 Andreas Kopp

我们感谢来自 NVIDIA 和 MONAI 团队的 Brad Genereaux、Prerna Dogra、Kristopher Kersten、Ahmed Harouni 和 Wenqi Li,他们在该资产的开发中给予了积极支持。

自 2021 年 12 月以来,我们发布了多个示例以支持使用 Azure 机器学习的医学影像,我们收到了热烈的反馈。兴趣和咨询进一步证明了 AI 如何迅速成为现代医学实践的关键方面。

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

选定的医学影像库用例(图片 5,6 通过 Shutterstock 获得 Andreas Kopp 授权

3D 脑肿瘤分割应用案例

今天,我们介绍一个新的医学影像资产,用于3D 脑肿瘤分割,这是一个解决肿瘤学领域中的挑战性用例的解决方案。我们的解决方案利用来自多个 MRI 模态的体积视觉输入和不同的 3D 胶质瘤肿瘤分割,以产生准确的肿瘤边界和子区域预测。为了处理涉及的大量影像数据,我们的资产采用了在 Azure ML 上使用可扩展 GPU 资源的并行训练。此外,我们利用了医学人工智能开放网络(MONAI),这是一个领域特定的框架,提供了最先进的工具和方法用于医学影像分析。

结合 Azure 机器学习和 MONAI,为可扩展的机器学习开发和操作提供了宝贵的协同效应,特别是在医学影像方面的创新。

Azure 机器学习是一个基于云的平台,为数据科学家和机器学习工程师提供了一个协作环境,用于大规模开发和部署机器学习模型。它提供了多种创作选项,从无需编码/低编码的自动化机器学习到使用 VSCode 等流行工具的代码优先。用户可以访问可扩展的计算资源进行训练和部署,使其在处理大型数据集和复杂模型时非常理想。此外,它包括 MLOps 功能,可以实现机器学习资产的管理、维护和版本控制,并允许自动化的训练和部署管道。该平台包括负责任的 AI 工具,可以帮助解释模型并减轻潜在的偏见,使其成为希望开发和部署伦理 AI 解决方案的企业的理想选择。

医学人工智能开放网络(MONAI)是一个基于 PyTorch 的开源项目,旨在用于医学影像。它提供了一套全面的工具,用于构建和部署医疗影像的 AI 模型:

  • MONAI 标签用于 AI 辅助标记医学影像数据

  • MONAI 核心用于训练具有领域特定功能的 AI 模型

  • MONAI 部署用于打包、测试和部署医学 AI 应用程序。

MONAI Core 是本文描述的解决方案的重点。它原生支持常用的医学成像格式,如 Nifty 和 DICOM。它还包括字典变换等功能,以确保在复杂的变换管道中图像和分割之间的一致性。此外,MONAI Core 提供了一系列网络架构,包括像 UNETR 这样的最先进的基于 Transformer 的 3D 分割算法。

欢迎探索我们的演示,并将其作为您自己 3D 分割用例的模板(无论是医学领域还是其他领域)。

我们使用的是 2021 版本的 脑肿瘤分割 (BraTS) 挑战 数据集。它是来自不同机构的专家标注的多模态 3D 磁共振成像 (MRI) 扫描图像的集合。MRI 是一种医学成像技术,利用磁场和无线电波的组合来可视化体内结构。通过改变成像参数可以生成不同的 MRI 模态,从而产生不同的组织对比度,使得某些特征在图像中更为突出。BraTS 数据集使用了以下模态:

  1. 原生 T1 (T1):可用于区分各种组织类型和病理状态。原生 T1 通常用于提取有关组织特性的定量信息。

  2. T1 加权对比剂钆 (T1Gd):这种模态可用于划分肿瘤边界和识别活跃肿瘤生长区域。

  3. T2 加权 (T2):这些图像对检测水肿(液体积聚)、炎症和其他可能与肿瘤相关的脑组织变化非常有用。

  4. T2 加权液体衰减反转恢复 (T2-FLAIR):T2-FLAIR 图像对于识别侵袭性肿瘤边缘和非增强肿瘤成分非常有用。

以下插图提供了 BraTS 数据集中专家标注的肿瘤分割示例:肿瘤核心、整体肿瘤和增强结构。左上角的图像结合了这三种分割。

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

从 BraTS 2021 数据集中提取的脑肿瘤分割(作者提供的图像)

能够区分这些结构对于诊断、预后和治疗规划至关重要。在 BraTS 数据集中,整体肿瘤指的是肿瘤的全部体积,包括核心部分和任何周围的水肿或肿胀。核心是肿瘤内部含有最具侵袭性的癌细胞的区域,即坏死和活跃的肿瘤。增强结构是指肿瘤区域在 MRI 扫描中出现明亮并在对比剂作用下增强的部分。这一区域与最活跃和侵袭性的癌细胞相关。

付诸实践

现在,我们已经对使用案例有了概述,让我们来看一下如何使用 Azure Machine Learning 和 MONAI 来训练和部署一个机器学习模型以完成这个有趣的任务。

这个使用案例的端到端工作流程在我们的 编排笔记本 中实现。这些是主要步骤及其相关输出:

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

脑肿瘤分割工作流程及输出(图片由作者提供)

第一步涉及下载和可视化数据,然后提交训练作业到 Azure ML 计算集群。模型训练完成并注册后,它被部署为 Azure ML 管理的端点。笔记本最后通过可视化验证集中的图像体上的模型预测来结束。训练和推理脚本单独存储在仓库中。

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

在 itkwidgets 3D 查看器中可视化肿瘤核心(图片由作者提供)

为了在我们的工作流程中管理机器学习资产,如数据集、计算资源、实验和结果模型,我们使用 Azure ML SDK 创建与 Azure ML 工作区的连接。这个连接可以在任何 Python 环境中建立,无需运行 Azure ML 计算实例。通过使用 Azure ML 管理资产,它们可以在 Azure ML Studio 中找到以备将来参考。这有助于轻松比较不同的实验,并通过引用相关资产(数据集版本、训练代码、环境等)创建结果模型的沿袭。

数据是通过 Kaggle API 从 Kaggle 下载的,并注册为 Azure ML 管理的数据集。这有助于对训练数据进行版本控制,这在训练数据可能随时间变化的迭代模型开发场景中尤为重要。例如,BraTS 数据集自 2012 年以来持续更新。将数据版本与模型关联可以确保良好的治理。

训练脚本 使用 MONAI 框架以及原生 PyTorch 类。数据使用 MONAI 框架提供的多个转换进行预处理,主要使用基于字典的包装器。训练图像的不同 MRI 模态被存储为单独的文件,分割掩膜则有一个文件。基于字典的包装器允许对所有训练数据使用单一的预处理管道,其中管道中的每个转换都可以指定应用于数据的哪一部分。

以下示例说明了 MONAI 字典变换在确保图像与相关分割标签之间的一致性方面的好处。假设我们使用一个随机变换的管道来进行数据增强,这些变换会影响图像的形状或位置(如调整大小、翻转、旋转、透视调整等)。我们必须确保对分割图像应用相同的变换,以确保图像和标签之间的一致性,便于后续的训练过程。在这种情况下,MONAI 字典变换是一个便利的工具:我们只需指定变换应应用于图像、标签还是两者。在这个例子中,我们将变换应用于两个对象。MONAI 还确保在随机变换的情况下保持一致性。

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

随机水平翻转的字典变换示例(图像由作者提供)

MONAI 提供了一系列基于 PyTorch 的网络架构。我们选择了 SegResNet(一个为分割任务适配的 ResNet 架构变体)来完成我们的任务。它基于编码器-解码器框架,其中编码器从输入图像中提取特征,解码器重建输出分割掩模。

# Specify SegResNet for 3D segmentation
segresnet = SegResNet(
    blocks_down=[1, 2, 2, 4],
    blocks_up=[1, 1, 1],
    init_filters=16,
    in_channels=4,
    out_channels=3,
    dropout_prob=0.2,
).to(device)

# Wrap network for DDP
model = torch.nn.parallel.DistributedDataParallel(module=segresnet, device_ids=[local_rank])

# Specify Dice loss, optimizer and LR scheduler
loss_function = DiceLoss(smooth_nr=0, smooth_dr=1e-5, squared_pred=True, to_onehot_y=False, sigmoid=True)
optimizer = torch.optim.Adam(model.parameters(), initial_lr, weight_decay=1e-5)
lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=max_epochs)

用于下采样的编码器块数量由 blocks_down 参数指定。因此,解码器部分的上采样块由 blocks_up 参数定义。第一个卷积层使用了 16 个滤波器。随着网络的深入,数量通常会翻倍,以便学习更复杂的特征。网络接受 4 个通道的输入,由 in_channels=4 指定。这代表了我们的多模态 MRI 数据,每个通道对应不同的 MRI 模态:T1、T1Gd、T2 和 T2-FLAIR。网络的输出具有 3 个通道,由 out_channels=3 指定。这对应于我们分割任务的类别:整个肿瘤、肿瘤核心和增强结构。

使用 PyTorch 原生特性以支持多 GPU 的分布式数据并行(DDP)训练。

对于损失函数,我们利用了 MONAI 的 Dice 损失。它是用来衡量预测分割与真实标签之间相似度的常用损失函数。它在类分布不均的情况下特别有用,例如医学图像分割,其中目标区域(如肿瘤)通常比背景小得多。

Adam 优化器与余弦退火学习率调度器(CosineAnnealingLR)一起使用。该调度器使学习率在初始值和指定的最小值之间振荡。CosineAnnealingLR 通过使模型在训练的初期阶段能够逃离局部最小值,并在后期阶段进行精确调整,从而帮助提高模型性能。

训练脚本通过命令作业提交到 Azure ML 计算集群,使用以下定义:

job= command(
    inputs= {"input_data": Input(type=AssetTypes.URI_FOLDER, path= dataset_asset.path)},
    code= 'src/',
    command= "python train-brats21.py --epochs 50 --initial_lr 0.00025 --train_batch_size 1 --val_batch_size 1 --input_data ${{inputs.input_data}} --best_model_name BRATS21",
    environment= "monai-multigpu-azureml@latest", 
    compute= train_target,
    experiment_name= experiment,
    display_name= f"3d brain tumor segmentation based on BRATS21",
    description= "## Brain tumor segmentation on 3D MRI brain scans",
    shm_size= '300g',
    resources= dict(instance_count= 1), # cluster nodes 
    distribution= dict(type="PyTorch", process_count_per_instance= 4), # GPUs per node
    environment_variables= dict(AZUREML_ARTIFACTS_DEFAULT_TIMEOUT = 1000),
    services= {
    "My_jupyterlab": JobService(job_service_type="jupyter_lab"),
    "My_vscode": JobService(job_service_type="vs_code",),
    "My_tensorboard": JobService(job_service_type="tensor_board",),
        })

returned_job= ml_client.create_or_update(job)

当我们提交训练作业时,会在计算集群中启动一个容器,使用我们之前在笔记本中定义的环境“monai-multi-gpu”。这个环境是从包含训练脚本要求的 conda 规范文件构建的。生成的 Docker 镜像被缓存到链接到 Azure ML 工作区的 Azure 容器注册表中。

通过使用distribution 参数,我们启动了分布式数据并行训练的进程数量。由于我们在这次实验中使用了一台四 GPU 的机器,因此启动了四个进程,每个进程访问自己的 GPU 实例。此外,我们还可以通过使用resources 参数在计算集群的不同节点上并行化训练运行。

了解 GPU 利用率对于优化资源使用和防止在长时间的 PyTorch 训练过程中出现的内存溢出(OOM)错误至关重要。Azure ML 提供了一系列指标来跟踪网络和磁盘 I/O,以及 CPU 和 GPU 内存和处理器利用率。以下示例演示了在 Standard_NC24rs_v3 类型的多 GPU 集群节点上训练运行开始时的 GPU 内存和能耗情况。在这种情况下,我们可以看到四个 GPU 上每个 GPU 的 16 GB 内存正在有效利用。剩余的头部空间很小,这清楚地表明增加批量大小可能会提高遇到 OOM 错误的风险。此外,Azure ML 还允许我们监控每个 GPU 的能耗,以千焦耳为单位进行测量。

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

监控训练期间的多 GPU 资源消耗(图像由作者提供)

使用大量数据集进行训练可能需要相当长的时间,可能需要几个小时甚至几天才能完成。使用像 Azure ML 这样的基于云的数据科学和机器学习平台的好处在于能够根据需要按需访问强大的资源。

对于我们的最终模型,我们使用了 NC96ads_A100_v4 集群节点,配备 96 核心、880 GB CPU 内存以及每个四个 NVIDIA A100 GPU 上 80 GB 内存,以训练超过 150 个 epoch。这种设置的主要优势是资源按需可用:一旦训练完成,集群会自动关闭,消除了任何进一步的费用。

在我们的案例中,我们选择了“低优先级” SKU,相比专用计算资源,它提供了更具成本效益的解决方案。然而,这种选择存在风险,即如果计算资源被用于其他任务,正在运行的训练作业可能会被抢占。

在训练作业运行时,JupyterLab、VSCode 和 Tensorboard 在容器中运行,使其可以进行监控和调试。这在 services 参数中指定。我们在训练脚本中使用 Tensorboard 和 MLFlow 记录训练指标,并使用 MLFlow 注册最终模型。由于 Azure ML 中对 MLFlow 的本机支持,指标和模型都可以在 Azure ML Studio 的实验部分找到。通过访问运行在容器中的 Tensorboard 实例,我们可以在模型训练时查看训练和验证指标。

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

使用 Tensorboard 进行交互式作业的训练性能监控(图像由作者提供)

Dice 系数用于评估模型性能,是一种常用于对象分割模型的度量指标。它衡量预测分割掩膜与真实数据掩膜之间的相似性。Dice 系数为 1.0 表示预测掩膜与真实数据掩膜完全重叠。对数据集中不同类别的 Dice 指标进行跟踪:肿瘤核心 (val_dice_tc)、整体肿瘤 (val_dice_wt) 和增强结构 (val_dice_et)。还跟踪不同类别的平均 Dice 指标 (val_mean_dice)。进行 150 个 epochs 的实验得到以下指标:

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

在 Azure ML Studio 中进行 150 个 epoch 训练期间的验证 DICE 指标(图像由作者提供)

注册模型后,我们进入部署阶段,这是使模型在生产环境中可用于预测新 MRI 图像的分割掩膜的一种方式。一个可能的脑肿瘤分割部署场景是将其与 MRI 图像查看器集成,以提供诊断辅助。在笔记本中,使用了 Azure ML 管理端点来进行部署。首先,创建一个管理在线端点,它提供了发送请求和接收模型推断输出的接口,并提供认证和监控功能。然后,我们通过定义模型、评分脚本、环境和目标计算类型来创建一个部署。我们参考了工作区中最新注册的 BraTS 模型进行此部署。

在将四种模态的二进制编码版本的 MRI 图像堆栈发送到我们的端点后,我们会收到作为 JSON 响应的一部分的预测分割结果。编排笔记本的最后部分包含了可视化这些预测的代码。我们的图像滑块将每个切片的预测分割结果与真实数据并排显示,以便进行比较。

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

比较专家标注的(真实数据)与预测的肿瘤结构(图像由作者提供)

人工智能在医疗保健中的负责任使用

将机器学习融入临床实践,例如肿瘤检测和分析,带来了诸多好处。例如,算法可以通过标记每日生成的海量图像中的可疑区域来辅助放射科医生。另一个应用是对肿瘤进展进行持续监测,以评估癌症治疗的效果。从每位患者的研究中计算出超过一百张切片的肿瘤体积可能非常耗时,但 AI 辅助测量可以显著加快这一过程。

尽管 AI 辅助图像分析取得了显著进展,但仍可能出现错误。未被检测出的肿瘤是一个重大问题,训练数据中稀有肿瘤或代表性不足的患者特征可能导致此类假阴性。因此,必须通过纳入多样化的数据集和不断完善算法来解决这些限制。

医生必须继续对诊断和治疗决策负责。机器学习预测应被视为有价值的支持工具,而不是可以替代人类专业知识的绝对替代品。医生始终处于整个过程的前沿,确保患者护理同时受到技术和专业判断的指导。

结论

在这篇文章中,我们探讨了 Azure 机器学习和 MONAI 在 3D 脑肿瘤分割中的应用,这是肿瘤学领域中的一个挑战性用例。我们讨论了 MRI 图像的各种模态以及区分肿瘤核心、整个肿瘤和增强结构在诊断、预后和治疗计划中的重要性。我们还概述了这一用例的端到端工作流程,包括数据可视化、模型训练和注册、部署以及模型预测的可视化。使用 Azure 机器学习和 MONAI 使我们能够利用可扩展的 GPU 资源进行并行训练,并为医疗成像中的 AI 模型训练提供了特定领域的能力。

我们希望 我们的资产 能作为未来医学领域 3D 分割用例的模板,并为现代医学实践中 AI 的日益重要性做出贡献。

该资产的训练代码基于 MONAI 3D 脑肿瘤分割教程 的早期版本。

参考文献

[1]: Hatamizadeh, A., Nath, V., Tang, Y., Yang, D., Roth, H. and Xu, D., 2022. Swin UNETR: Swin Transformers for Semantic Segmentation of Brain Tumors in MRI Images. arXiv preprint arXiv:2201.01266.

[2] Tang, Y., Yang, D., Li, W., Roth, H.R., Landman, B., Xu, D., Nath, V. 和 Hatamizadeh, A., 2022. 自监督预训练的 swin 变换器用于三维医学图像分析。见于 IEEE/CVF 计算机视觉与模式识别会议论文集(第 20730–20740 页)。

[3] U.Baid 等人,RSNA-ASNR-MICCAI BraTS 2021 脑肿瘤分割和放射基因组分类基准,arXiv:2107.02314,2021。

[4] B. H. Menze, A. Jakab, S. Bauer, J. Kalpathy-Cramer, K. Farahani, J. Kirby 等人,“多模态脑肿瘤图像分割基准(BRATS)”,《IEEE 医学影像学报》34(10),1993–2024(2015)DOI: 10.1109/TMI.2014.2377694

[5] S. Bakas, H. Akbari, A. Sotiras, M. Bilello, M. Rozycki, J.S. Kirby 等人,“通过专家分割标签和放射组学特征推动癌症基因组图谱胶质瘤 MRI 收集”,《自然科学数据》,4:170117(2017)DOI: 10.1038/sdata.2017.117

[6] S. Bakas, H. Akbari, A. Sotiras, M. Bilello, M. Rozycki, J. Kirby 等人,“TCGA-GBM 收集的术前扫描的分割标签和放射组学特征”,癌症影像档案,2017. DOI: 10.7937/K9/TCIA.2017.KLXWJJ1Q

多模态思维链:在多模态世界中解决问题

原文:towardsdatascience.com/multimodal-chain-of-thoughts-solving-problems-in-a-multimodal-world-961a8ab9d0fa

NLP | 多模态性 | 思维链 |

世界不仅仅是文字:如何将思维链扩展到图像和文字?

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

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

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

图片由 Giulio Magnifico 提供,来源于 Unsplash

有时得出答案并不容易,特别是当问题需要推理时。模型并不总是在其参数中隐藏答案,但可以通过正确的上下文和方法得出答案。什么是思维链?为什么这种方法使得解决多步骤推理任务成为可能?它可以扩展到多模态问题(即包含图像和文字的问题)吗?只有大型模型才能做到这一点吗?

本文讨论了如何回答这些问题。

思维链(CoT):它是什么?

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

图片由 Todd Cravens 提供,来源于 Unsplash

近年来,我们见证了模型参数数量的增长(超过 1000 亿个参数)。这受到扩展法则的推动:随着参数数量的增加,误差减少。

## 无监督数据剪枝:用更少的数据更好地学习

更多的数据并不总是意味着更准确的模型,但如何选择你的数据呢?

towardsdatascience.com

虽然这对于如情感分析和机器翻译等任务是正确的(即使在零-shot少-shot 学习的情况下),即使是拥有数十亿参数的模型在需要多步推理的任务中(例如数学问题或常识推理)也会遇到困难。

如何让模型在这些任务中取得成功?

大型模型可以针对特定任务进行微调,这也是最初尝试的系统。正如这个想法的作者解释的那样,如果你问一个模型鲸鱼是否有肚脐,模型将错误地回答“没有”。这是因为模型的参数中没有存储这条信息。作者建议可以通过提供隐性知识的提示来帮助模型:“鲸鱼是哺乳动物”。

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

来源:这里

提供隐性知识的想法为系统通过与用户交互来改进自己铺平了道路。用户可以识别错误并向模型提供信息,允许其自我纠正。或者更准确地说,正如作者所定义的:

这可以视为一种“单次学习”的形式,可以在不进行进一步训练的情况下即时改进模型,这与大多数当前依赖于数据收集和重新训练来修正模型错误的工作方式不同。

从概念上讲,这个想法是一个模型可以通过利用中间步骤来解决其确切答案并不直接知道的问题。

正如谷歌所指出的,提示使得上下文少样本学习成为可能。换句话说,除了对特定任务进行微调外,可以通过一些输入-输出示例来提示语言模型。这种方法被证明非常有效,尤其在问答系统中。此外,正如在上下文学习中所示,它对于大型模型特别有效

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

“对于所有 42 个准确度基准的整体表现,虽然零-shot 性能随着模型规模的增加而稳步提升,但少-shot 性能则更快增加,证明了更大的模型在上下文学习方面更为高效。” 来源:这里

谷歌随后提出,通过仅包含几个思维链的示例,可以让模型解决多步推理问题。为了更好地理解,以下是经典提示和思维链提示之间的变化示例:

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

“思维链提示使大型语言模型能够处理复杂的算术、常识和符号推理任务。思维链推理过程被突出强调。” 来源:这里

这种方法的优点是它既不需要改变语言模型的权重,也不需要大规模的训练数据集。

简而言之,我们可以说,这个理念是将复杂的问题分解为一系列可以单独解决的中间步骤。

这可能看起来是小事,但实际上意味着这种方法可以应用于任何你可以用语言解决的问题。

谷歌的作者表示这是模型的一个突现特性,它在达到一定的模型容量时(他们估计约 100 B 参数)出现。作者评估了增加模型以解决数学问题:

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

“: 思维链提示使大型语言模型能够解决具有挑战性的数学问题”。来源:这里

此外,作者指出,模型的改进并非来自增加参数,而是通过使用“思维链提示,增加模型规模会带来显著优于标准提示的大模型性能提升。”

这对于常识推理(“在假设一般背景知识的情况下推理关于物理和人类互动”)也是成立的。

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

“: 算术、常识和符号推理基准的输入、思维链、输出三元组示例。” 来源:这里

在这种情况下,模型也显示了相同的行为:“性能随着模型规模的扩大而提高,采用思维链提示也带来了额外的小幅改进。” 最大的改进出现在运动理解领域(令人惊讶)。

总的来说,我们已经看到 CoT 有两种技术,微调或使用提示(上下文学习)。关于第二种范式,我们可以进一步细分为:

  • 零样本 CoT。Kojima 证明了语言模型在零样本 CoT(仅仅添加“让我们一步步来思考”)方面表现不错,这足以显著提高零样本 LLM 的复杂推理能力。

  • 少样本 CoT。少量的逐步推理示例用于模型推理中的条件设置。每个示例同时提供一个问题和一个解释模型如何得出最终答案的推理链(这些示例可以是手工制作的或使用自动生成)。

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

来源:这里

少样本 CoT 已被证明更有效且结果更好(前提是示例编写得很好)。因此,大多数后续研究都集中在这种方法上。

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

“典型的 CoT 技术(FT: 微调;KD: 知识蒸馏)。第一部分:上下文学习技术;第二部分:微调技术。根据我们所知,我们的工作是首次研究不同模态下的 CoT 推理。此外,我们专注于 1B 模型,不依赖于 LLMs 的输出。” 来源:这里

多模态链式思维具有挑战性

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

图片来源于 airfocus 在 Unsplash 上

正如我们所见,链式思维(CoT)在需要复杂推理的问题上证明了非常有用。许多问题不仅是文本的,还有多模态的。例如,解决一个问题我们可能需要查看图片。正如我们所说,CoT 仅适用于可以用文本形式表达的问题。我们如何处理多模态问题?

想象一下阅读一本没有图形或表格的教科书。我们通过联合建模不同的数据模态(如视觉、语言和音频)来极大地增强知识获取能力。 (来源)

最近一篇文章正好提出了这个问题,并试图将 CoT 扩展到多模态问题中:

[## 多模态链式思维推理在语言模型中的应用]

大型语言模型(LLMs)通过利用链式思维在复杂推理上表现出色…

arxiv.org

如前所述,参数少于 100 亿的模型往往会产生不合逻辑的 CoT,从而导致错误的答案。一个 多模态模型 不仅要处理文本输入,还要处理其他模态。这使得创建一个参数少于 100 亿的模型变得困难。

另一方面,META 的 LLaMA 显示出,参数少于 100 亿的模型可以达到与更大模型相当的结果。

[## META 的 LLaMA:一个击败巨头的小型语言模型]

META 开源模型将帮助我们理解语言模型的偏见如何产生

medium.com](https://medium.com/mlearning-ai/metas-llama-a-small-language-model-beating-giants-5065948e0b7f?source=post_page-----961a8ab9d0fa--------------------------------)

此外,正如其他研究所示,文本模型在训练过程中没有看到图片,因此没有关于视觉元素或如何利用视觉特征的信息。

在多模态环境中进行 CoT 推理要求模型考虑不同的模态:给定不同模态的输入,模型将一个多步骤的问题分解为一系列中间步骤,然后推断出答案。

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

多模态 CoT 任务示例。 来源: 这里

执行多模态 CoT 的最直接方法是将不同模态的输入转换为一种模态,并提示大型语言模型执行 CoT。 (source)

例如,可以取一张图像,并将其作为输入用于字幕生成模型。一旦获得字幕,就可以将其与文本提示结合起来,然后提供给大型语言模型。

然而,这种方法有一个严重的缺陷,即字幕与视觉特征相比丢失了大量信息,因此不同模态间的信息协同作用丧失了。

此外,之前的研究表明,预训练的单模态模型的跨模态对齐并不容易。例如,在 BLIP-2 中,为了使视觉变换器和语言模型能够互相交流,他们需要在两者之间增加一个额外的变换器。

## BLIP-2: when ChatGPT meets images

BLIP-2 是一种新的视觉语言模型,能够进行关于图像的对话。

levelup.gitconnected.com

考虑到这些挑战,作者决定研究是否可以训练一个具有 10 亿参数的多模态 CoT 模型。

这项工作集中在 10 亿模型上,因为它们可以用消费级 GPU(例如 32G 内存)进行微调和部署。在这一部分,我们将研究为什么 10 亿模型在 CoT 推理中失败,并研究如何设计有效的方法以克服这一挑战。 (source)

为什么小模型在 CoT 中失败,如何设计以克服这一挑战?

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

图片由 Jason Leung 提供,来源于 Unsplash

实际上,训练小模型进行推理的方法已经被尝试过。然而,之前的尝试中使用了一个大型模型作为教师和一个小型模型作为学生。

例如,作者为教师模型提供了一个提示,并使用了“让我们一步一步思考”的方法来获得解释推理的答案。然后,将提示加上演示提供给较小的模型。

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

“我们考虑一种由多个阶段组成的方法。首先,使用多步骤推理提示一个大型教师模型回答问题,而不依赖于正确的示例。也就是说,教师采用零-shot 链式思维推理生成输出。然后,我们使用生成的推理样本(包括问题和教师输出)对更小的学生模型进行微调。” 图像来源 (here)

然而,这种方法仍然需要使用大型语言模型及其所有缺点。

作者决定探索小型模型可以针对多模态-CoT 进行微调的可能性。简而言之,融合多模态特征允许模型架构更加灵活地调整(与提示相关)。然而,主要问题仍然存在:“关键挑战在于,参数少于 100 亿的语言模型往往生成幻觉理由,从而误导答案推断。”

首先,为什么小模型在 CoT 推理中会出现幻觉?

作者提出了相同的问题:调查为什么 1-B 模型在 CoT 推理中失败。一旦理解了这一点,研究有效的方法。

作者首先对文本-only 基线模型进行了 CoT 推理的微调。在这种情况下,问题被建模为文本生成问题。基线包括问题(Q)、上下文(C)和多个选项(O),模型必须预测答案(A)。作者将基线与在答案前预测理由(R)(QCM→RA)以及理由用于解释答案(QCM→AR)进行了比较。

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

(source)

结果令人惊讶,如果模型首先预测理由,准确率下降超过 10%:“结果表明,理由可能不一定有助于预测正确答案。”换句话说,似乎推理反而对答案有害。

但为什么呢?

为了理解这一点,作者决定将问题分成两个阶段。首先,生成理由,然后利用这些理由回答问题。模型在生成高质量理由方面成功了(RougeL是一种用于自动摘要和机器翻译的度量),但同时,似乎对准确性推断(问题的答案)产生了不利影响。

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

(source)

理由并没有帮助提高答案的准确性。因此,作者选择了 50 个随机错误案例并手动检查。发现模型在生成理由时经常出现幻觉,因为缺乏对视觉内容的参考。

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

(source)

这是最常见的错误,超过 60%的错误归因于这一因素。

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

(来源)

那么为什么不提供关于图像内部内容的信息呢? 作者使用了一个管道来生成标题并将其提供给模型(将标题附加到输入中)。然而,这导致边际准确度的增加(0.59 个百分点,在表 3 中)。

作者随后测试了另一种方法,将图像作为输入传递给DETR模型,目的是提取视觉特征。他们将这些视觉特征与编码后的语言表示结合在一起。换句话说,文本由 LM 编码器编码,图像由视觉模型编码。这两个输出结合在一起,成为 LM 解码器的输入。

结果显示(见表 3),这不仅改善了理由生成,还提高了回答的准确性。换句话说,拥有更好的理由“幻觉现象得到了缓解。”视觉特征对更好的回答有益,但这些有用的信息可能在生成标题的过程中丢失了。

理解了模型为什么会出现幻觉之后,我们可以使用什么框架来进行高效的多模态-CoT?

作者建议将语言(文本)和视觉(图像)模态结合到一个两阶段框架中:首先生成理由,然后生成回答。

模型架构在两个步骤中是相同的;然而,输入和输出有所变化。在第一步中,模型接受语言和视觉输入以生成理由。在第二步中,提供了原始语言输入,并将其附加到第一阶段生成的理由中。这经过第二模型的编码器,然后添加视觉特征并使用解码器得到最终答案。

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

“我们多模态-CoT 框架的概述。Multimodal-CoT 由两个阶段组成:(i)理由生成和(ii)答案推断。两个阶段共享相同的模型架构,但在输入和输出上有所不同。在第一阶段,我们将语言和视觉输入提供给模型以生成理由。在第二阶段,我们将原始语言输入与第一阶段生成的理由附加在一起。然后,我们将更新后的语言输入与原始视觉输入一起提供给模型以推断答案。” (来源)

小模型能有竞争力吗?

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

照片由Steven Lelham拍摄,来自 Unsplash

我们已经了解了为什么小模型在 CoT 过程中会产生幻觉,如何解决这个问题,现在还需要了解这种方法是否在与大型模型和其他方法相比时具有竞争力。

作者决定使用 ScienceQA 基准:

ScienceQA 是首个大规模的多模态科学问题数据集,注释了详细的讲解和解释。它包含 21,000 个多模态选择题,涵盖了 3 个学科、26 个主题、127 个类别和 379 项技能的丰富领域多样性。 (来源)

为了使用视觉特征,他们需要一个使用编码器-解码器的模型,因此选择了T5。此外,为了更好地研究该方法是否对其他模型具有通用性,他们还选择了FLAN-T5。他们还决定将其与多个模型和人类进行比较。

结果表明,他们的方法优于 GPT-3.5,并且在各种问题类别中也超越了人类(无论是平均水平还是在不同类别中)。UnifiedQA 和 GPT-3.5 使用了字幕,结果表明视觉特征更为有效。

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

(来源)

消融研究表明,使用两阶段方法能够充分发挥视觉特征的优势。

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

(来源)

此外,作者指出,多模态性能提升收敛性。实际上,这种两阶段模型从训练开始时就能实现更高的准确性。

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

“No-CoT 基准和 MultimodalCoT 变体在各个时期的准确性曲线。” (来源)

作者表示,该方法具有广泛的模型通用性来提取视觉特征,他们选择了 DETR,因为它提供了最佳的准确性。

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

(来源)

选择的文本模型也是具有通用性的。也就是说,该方法即使在使用不同的语言模型时也能有效。

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

(来源)

然后,作者检查了 50 个答案正确的例子和 50 个答案错误的例子,以更好地理解机制。结果表明,CoT 并不总是对答案有利,但模型非常稳健,在某些情况下即使推理错误也能正确回答。此外,当答案错误时,大多数错误是由于常识性错误。

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

(来源)

当问题需要常识知识时,该模型在大多数情况下会出现常识错误:例如,理解地图或计算图像中的数字,或使用字母表。错误示例:

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

(source)

作者表示,这些结果为模型的未来修改提供了线索:

可以通过以下方式改进 MultimodalCoT:(i)结合更有信息量的视觉特征,改善语言-视觉交互,以便理解地图和计数;(ii)注入常识知识;(iii)应用过滤机制,例如,只使用有效的 CoT 来推断答案,并去除无关的 CoT。(source)

作者已将模型、代码和数据集上传到 GitHub,供希望测试或了解更多的人使用:

[## GitHub - amazon-science/mm-cot: “Multimodal Chain-of-Thought Reasoning”的官方实现…]

“想象一下学习一本没有图表的教科书。”Multimodal-CoT 在解耦的方式中融入了视觉特征…

github.com](https://github.com/amazon-science/mm-cot?source=post_page-----961a8ab9d0fa--------------------------------)

告别思考

本研究中的作者正式研究了多模态 CoT。他们分析了为什么小模型在 CoT 期间会产生幻觉,并展示了小模型在多模态 CoT 中能够超越大型模型(甚至超越人类表现)的能力。关键是能够最佳地结合文本和视觉模态。

这是通过使用两阶段方法实现的,第一阶段使用视觉特征创建推理,然后利用这一最佳推理来获得答案。作者进行的分析给出了如何获得更好模型的建议。

简而言之,这篇论文的结果表明,即使是一个小模型也能解决复杂问题。此外,为模型提供正确的多模态特征是至关重要的。无需一个拥有数十亿参数的大型语言模型,因为对视觉特征有了解的小模型在图像描述方面表现得更好。

如果你觉得这些内容有趣:

你可以查看我的其他文章,也可以订阅以便在我发布新文章时获得通知,你还可以在LinkedIn上联系我

这是我的 GitHub 仓库的链接,我计划在这里收集与机器学习、人工智能等相关的代码和许多资源。

[## GitHub - SalvatoreRa/tutorial: 机器学习、人工智能、数据科学教程…]

机器学习、人工智能、数据科学的教程,包含数学解释和可重用的代码(使用 Python…)

GitHub 教程

或许你对我最近的一篇文章感兴趣:

[## PCA:生物信息学家最喜爱的工具可能会产生误导

一项新研究评估了一个最常用的技术可能带来的问题

PCA:生物信息学家最喜爱的工具可能会产生误导 [## 稳定扩散与大脑:AI 如何读取我们的思想

研究人员能够利用 fMRI 数据重建图像。

稳定扩散与大脑:AI 如何读取我们的思想 [## 微软 BioGPT:走向生命科学领域的 ChatGPT

BioGPT 在不同的生物医学 NLP 任务中达到了 SOTA

微软 BioGPT:走向生命科学领域的 ChatGPT [## 稳定扩散填补医学图像数据中的空白

一项新研究显示,稳定扩散可以帮助医学图像分析和罕见疾病。怎么做?

稳定扩散填补医学图像数据中的空白

R 中的多项逻辑回归

原文:towardsdatascience.com/multinomial-logistic-regression-in-r-428d9bb7dc70

R 中的统计系列

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

·发布于 Towards Data Science ·6 分钟阅读·2023 年 1 月 29 日

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

图片由 Edge2Edge Media 提供,来源于 Unsplash

介绍

如果你阅读了我之前关于“R 中的统计系列”的文章,你可能对 R 中的逻辑回归实现有了较好的理解,也对不同类型的逻辑回归模型有了基本了解。我们在回归领域已经走了很长一段路,覆盖了二项、比例优势 (PO)、广义以及部分比例优势 (PPO) 模型。在这篇文章中,我将讨论多项逻辑回归,它是特定应用的,并且可能对理解具有多个无序响应变量的逻辑回归模型至关重要。例如,可能出现的回归类型是人们对政党的归属,例如共和党、民主党、独立党等。

简要回顾

首先,我们来回顾一下我们已经覆盖的内容。起初,我们介绍了二项逻辑回归,它没有有序响应。在这里,我们只有两个类别,例如健康或不健康。接着,我们深入探讨了具有多个有序响应的有序逻辑回归模型。在这里,我们对健康状态进行了更多有序分类,并将其输入到有序模型中。该模型的一个基本假设是系数独立于类别,并且它们在响应之间不变化。例如,如果我们有四个健康状态类别,并按 1、2、3、4 排列。我们假设每个独立变量的系数在这四个类别中保持不变。这被称为比例优势 (PO) 假设。

后来我们进入了广义逻辑模型,并允许所有系数在各类别之间变化。例如,当我们考虑健康状态从 1 变为 2 时,某个特定自变量的系数将与健康状态从 2 变为 3 时的系数值不同。

之前讨论过的另一种模型是部分比例奇数(PPO)模型。在这种模型中,仅允许违反比例奇数假设的变量在各类别之间变化。例如,如果我们将健康状态作为响应变量,教育年限、婚姻状态和家庭收入作为预测变量,如果我们发现只有教育状态违反了比例奇数假设,我们可以为此提出 PPO 模型,而不是广义有序回归模型。我知道这可能听起来有点复杂,但如果你查看我之前关于“R 语言统计系列”的文章,希望事情会变得更加易懂。目前,下面的表格展示了我们在模型定义和执行方面所涵盖的内容。

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

什么是多项式回归?

简单来说,多项式回归模型通过对数比或对数赔率方法估计个体落入特定类别的可能性,相对于基准类别。它类似于当名义响应变量有超过 2 个结果时对二项分布的扩展。在多项式回归中,我们需要定义一个参考类别,模型将确定相对于参考类别的多个二项分布参数。

在我们的示例中,我们将设置一个特定的健康状态作为基准类别,并以此进行多项式逻辑回归。简言之,多项式回归就像一次性执行多个二项回归模型。

数据集

UCI 仓库中的成人数据集将再次用于多项式逻辑回归的实现。下面是数据集的快速查看。

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

成人数据集来自 UCI 仓库

  • 教育:数值型和连续型

  • 婚姻状态:二元(0 为未婚,1 为已婚)

  • 性别:二元(0 为女性,1 为男性)

  • 家庭收入:二元(0 为平均或低于平均,1 为高于平均)

  • 健康状态:有序(1 为差,2 为一般,3 为好,4 为优秀)

在这里,尽管健康状态是有序的,但我们实际上不需要它,因为我们将执行多个二项逻辑回归。

R 语言中的实现

要实现多项式逻辑回归,我将使用 VGAM 包中的 vglm() 命令。代码片段如下。

如前所述,我们需要定义一个参考类别。在这里,我将健康状态水平=1 定义为参考类别。

结果解释

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

模型总结

好的。我们已经总结了上述定义的模型。这是一个具有多个结果类别的预测模型。预测变量是教育年限(连续变量),结果是类别变量,实际上是有序的,但我们不需要类似的有序响应。我们可以简单地将数值分配给这些类别。

总结与二元逻辑回归类似,但我们有多个二元回归模型,所有模型都与参考类别进行比较。让我们参考上述系数块。

第一个模型使用 intercept1 和 educ1 比较类别 2 与参考类别 1(模型定义中已定义)。第二个模型使用 intercept2 和 educ2 比较类别 3 与参考类别 1。第三个模型使用 intercept3 和 educ3 比较类别 4 与参考类别 1。由于有四个类别,因此只能进行三种二元比较。

model1:

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

model2:

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

model3:

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

相同的二元逻辑回归解释也适用于此。例如,在 model1 中,我们可以说,每增加一个单位的 educ 变量,类别 2 相对于类别 1 的对数几率增加 0.08969,其余两个模型也是如此。所有情况下的斜率都是正的,因为随着教育水平的提高,具有更好健康状态的对数几率也会增加。这是显而易见的,但我们通过给定的数据集量化了这一事实。

R 还对每个预测变量进行了假设检验。原假设声明预测变量在预测对数几率中不是显著变量。educ1 的相关 p 值为 0.00853,小于 0.01。因此,我们可以拒绝原假设,并得出结论,educ1 在确定类别 2 相对于参考类别 1 的对数几率中发挥了显著作用。

我们还可以在 R 中确定奇数比,如下所示。

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

奇数比

这基本上是系数的指数值。这告诉我们,每增加一个单位的 educ,处于类别 2 的几率相比于类别 1 增加了 1.09350 倍。换句话说,每增加一个单位的 educ,处于类别 2 的几率增加了 9.35%。

对于其他模型,百分比变得显著。每增加一个单位的教育,处于类别 3 的概率增加 25.23%,而每增加一个单位的教育,处于类别 4 的概率增加 35.82%,相对于参考类别 1。

结论

在这篇文章中,我讨论了多项逻辑回归模型的必要性,并在 R 中进行了实现。这种回归类型类似于二项回归,只是我们进行的是多个二元比较。当我们有像政党归属这样的无序类别时,可以进行多项逻辑回归。

数据集致谢

Dua, D. 和 Graff, C. (2019). UCI 机器学习库 [http://archive.ics.uci.edu/ml]。加州尔湾:加州大学信息与计算机科学学院 (CC BY 4.0)

[## 使用我的推荐链接加入 Medium - Md Sohel Mahmood

阅读 Md Sohel Mahmood(以及 Medium 上的其他成千上万名作者)的每一篇故事。您的会员费用将直接…

获取 Md Sohel Mahmood 发布时的电子邮件通知 [## 订阅 Md Sohel Mahmood 的所有故事

每当 Md Sohel Mahmood 发布新内容时,你将收到电子邮件通知。通过注册,如果你还没有 Medium 帐户,将创建一个…

订阅 Medium 使用我的推荐链接 - Md Sohel Mahmood

PyTorch 中的多 GPU 训练及其替代方案:梯度累积

原文:towardsdatascience.com/multiple-gpu-training-in-pytorch-and-gradient-accumulation-as-an-alternative-to-it-e578b3fc5b91

代码与理论

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

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

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

unsplash.com/photos/vBzJ0UFOA70

在这篇文章中,我们首先将探讨数据并行(DP)和分布式数据并行(DDP)算法的区别,然后解释什么是梯度累积(GA),最后展示 DDP 和 GA 如何在 PyTorch 中实现,并且它们如何导致相同的结果。

引言

在训练深度神经网络(DNN)时,一个重要的超参数是批量大小。通常,批量大小不应过大,因为网络会倾向于过拟合,也不应过小,因为这会导致收敛速度缓慢。

当处理高分辨率图像或其他占用大量内存的数据时,假设如今大多数大 DNN 模型的训练都是在 GPU 上进行的,根据可用 GPU 的内存,适配小批量大小可能会有问题。因为,如前所述,小批量大小会导致收敛缓慢,我们可以使用三种主要方法来增加有效批量大小:

  1. 使用多个小型 GPU 并行运行模型于小批量 — DP 或 DDP 算法

  2. 使用更大的 GPU(昂贵)

  3. 在多次步骤中累积梯度

现在让我们详细探讨 1. 和 3. — 如果你幸运地拥有一张可以容纳所有数据的大 GPU,你可以阅读 DDP 部分,了解它是如何在 PyTorch 中实现的,跳过其他部分。

假设我们想要有效的批量大小为 30,但每个 GPU 上只能容纳 10 个数据点(小批量大小)。我们有两个选择:数据并行或分布式数据并行:

数据并行(DP)

首先,我们定义主 GPU。然后,执行以下步骤:

  1. 将 10 个数据点(小批量)和模型的副本从 Master GPU 移动到其他 2 个 GPU。

  2. 在每个 GPU 上进行前向传播,并将输出传递给 Master GPU。

  3. 在 Master GPU 上计算总损失,然后将损失返回到每个 GPU,以计算参数的梯度。

  4. 将梯度(这些是所有训练样本的梯度平均值)返回到 Master GPU,将它们求和以获得整个 30 个数据点批次的平均梯度。

  5. 更新 Master GPU 上的参数,并将这些更新发送到其他两个 GPU 以进行下一次迭代。

这个过程存在一些问题和低效之处:

  • 数据从 Master GPU 传递,然后在其他 GPU 之间分割。此外,Master GPU 的利用率高于其他 GPU,因为总损失的计算和参数更新都发生在 Master GPU 上。

  • 我们需要在每次迭代时同步其他 GPU 上的模型,这可能会减慢训练速度。

分布式数据并行(DDP)

分布式数据并行(Distributed Data Parallel)旨在改善数据并行(Data Parallel)算法的低效问题。我们仍然使用之前的设置——每批次 30 个数据点,3 个 GPU。区别如下:

  1. 它没有 Master GPU。

  2. 由于我们不再拥有 Master GPU,我们直接从磁盘/RAM 上以 非重叠 的方式并行加载每个 GPU 上的数据——DistributedSampler 负责这项工作。在底层,它使用本地排名(GPU id)将数据分配到各个 GPU 上——给定 30 个数据点,第一个 GPU 将使用点 [0, 3, 6, … , 27],第二个 GPU [1, 4, 7, …, 28],第三个 GPU [2, 5, 8, … , 29]。

n_gpu = 3
for i in range(n_gpu):
  print(np.arange(30)[i:30:n_gpu])

前向传播、损失计算和反向传播在每个 GPU 上独立执行,梯度异步减少,计算均值,然后所有 GPU 上的更新进行。

由于 DDP 相对于 DP 的优势,现如今更倾向于使用 DDP,因此我们仅展示 DDP 实现。

梯度累积

如果我们只有一个 GPU,但仍想使用更大的批次大小,可以选择在一定步数内累积梯度,有效地累积一定数量的小批次的梯度,从而增加有效批次大小。以上述示例为例,我们可以累积 10 个数据点的梯度进行 3 次迭代,以实现与 DDP 训练中有效批次大小为 30 相同的结果。

DDP 过程 代码

下面我将仅讲述实现 DDP 与单 GPU 代码相比的区别。完整代码可以在下几节中找到。首先,我们初始化进程组,以允许不同进程之间进行通信。通过 int(os.environ[“LOCAL_RANK”]),我们可以检索给定进程中使用的 GPU。

init_process_group(backend="nccl")
device = int(os.environ["LOCAL_RANK"])
torch.cuda.set_device(device)

然后,我们需要将模型包装在 DistributedDataParallel 中,以启用多 GPU 训练。

model = NeuralNetwork(args.data_size) 
model = model.to(device) 

if args.distributed:
  model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[device])

最后一部分是定义 DistributedSampler,我在 DDP 部分提到过。

sampler = torch.utils.data.DistributedSampler(dataset)

其余的训练保持不变——我将在文章的末尾包括完整代码。

梯度累积代码

当发生反向传播时,在调用*loss.backward()后,梯度会存储在各自的 Tensor 中。实际更新发生在调用optimizer.step()时,然后存储在 Tensor 中的梯度会被optimizer.zero_grad()设置为零,以便进行下一次反向传播和参数更新。因此,为了累积梯度,我们调用loss.backward()进行所需的梯度累积次数,而不将梯度设置为零,以便它们在多个迭代中累积,然后我们对累积梯度迭代的平均梯度进行平均loss = loss/ACC_STEPS)。之后,我们调用optimizer.step()*并将梯度置零,以开始下一次梯度累积。在代码中:

ACC_STEPS = dist.get_world_size() # == number of GPUs
# iterate through the data
for i, (idxs, row) in enumerate(loader):
  loss = model(row)  
  # scale loss according to accumulation steps
  loss = loss/ACC_STEPS
  loss.backward()
  # keep accumualting gradients for ACC_STEPS
  if ((i + 1) % ACC_STEPS == 0):
    optimizer.step()  
    optimizer.zero_grad()

完整代码

import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0,1"
print(os.environ["CUDA_VISIBLE_DEVICES"])

import torch
import torch.nn as nn
from torch.utils.data import DataLoader, Dataset, Sampler
import argparse
import torch.optim as optim 
import numpy as np
import random
import torch.backends.cudnn as cudnn
import torch.nn.functional as F

from torch.distributed import init_process_group
import torch.distributed as dist

class data_set(Dataset):

    def __init__(self, df):
        self.df = df

    def __len__(self):
        return len(self.df)

    def __getitem__(self, index):    

        sample = self.df[index]
        return index, sample

class NeuralNetwork(nn.Module):
    def __init__(self, dsize):
        super().__init__()
        self.linear =  nn.Linear(dsize, 1, bias=False)
        self.linear.weight.data[:] = 1.

    def forward(self, x):
        x = self.linear(x)
        loss = x.sum()
        return loss

class DummySampler(Sampler):
    def __init__(self, data, batch_size, n_gpus=2):
        self.num_samples = len(data)
        self.b_size = batch_size
        self.n_gpus = n_gpus

    def __iter__(self):
        ids = []
        for i in range(0, self.num_samples, self.b_size * self.n_gpus):
            ids.append(np.arange(self.num_samples)[i: i + self.b_size*self.n_gpus :self.n_gpus])
            ids.append(np.arange(self.num_samples)[i+1: (i+1) + self.b_size*self.n_gpus :self.n_gpus])
        return iter(np.concatenate(ids))

    def __len__(self):
        # print ('\tcalling Sampler:__len__')
        return self.num_samples

def main(args=None):

    d_size = args.data_size

    if args.distributed:
        init_process_group(backend="nccl")
        device = int(os.environ["LOCAL_RANK"])
        torch.cuda.set_device(device)
    else:
        device = "cuda:0"

    # fix the seed for reproducibility
    seed = args.seed

    torch.manual_seed(seed)
    np.random.seed(seed)
    random.seed(seed)
    cudnn.benchmark = True

    # generate data
    data = torch.rand(d_size, d_size)

    model = NeuralNetwork(args.data_size)    
    model = model.to(device)  

    if args.distributed:
        model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[device])

    optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
    dataset = data_set(data)

    if args.distributed:
        sampler = torch.utils.data.DistributedSampler(dataset, shuffle=False)
    else:
        # we define `DummySampler` for exact reproducibility with `DistributedSampler`
        # which splits the data as described in the article. 
        sampler = DummySampler(dataset, args.batch_size)

    loader = DataLoader(
                dataset,
                batch_size=args.batch_size,
                num_workers=0,
                pin_memory=True,
                sampler=sampler,
                shuffle=False,
                collate_fn=None,
            )          

    if not args.distributed:
        grads = []

    # ACC_STEPS same as GPU as we need to divide the loss by this number
    # to obtain the same gradient as from multiple GPUs that are 
    # averaged together
    ACC_STEPS = args.acc_steps 
    optimizer.zero_grad()

    for epoch in range(args.epochs):

        if args.distributed:
            loader.sampler.set_epoch(epoch)

        for i, (idxs, row) in enumerate(loader):

            if args.distributed:
                optimizer.zero_grad()

            row = row.to(device, non_blocking=True) 

            if args.distributed:
                rank = dist.get_rank() == 0
            else:
                rank = True

            loss = model(row)  

            if args.distributed:
                # does average gradients automatically thanks to model wrapper into 
                # `DistributedDataParallel`
                loss.backward()
            else:
                # scale loss according to accumulation steps
                loss = loss/ACC_STEPS
                loss.backward()

            if i == 0 and rank:
                print(f"Epoch {epoch} {100 * '='}")

            if not args.distributed:
                if (i + 1) % ACC_STEPS == 0: # only step when we have done ACC_STEPS
                    # acumulate grads for entire epoch
                    optimizer.step()  
                    optimizer.zero_grad()
            else:
                optimizer.step() 

        if not args.distributed and args.verbose:
            print(100 * "=")
            print("Model weights : ", model.linear.weight)
            print(100 * "=")
        elif args.distributed and args.verbose and rank:
            print(100 * "=")
            print("Model weights : ", model.module.linear.weight)
            print(100 * "=")

if __name__ == "__main__":

    parser = argparse.ArgumentParser()
    parser.add_argument('--distributed', action='store_true',)
    parser.add_argument('--seed', default=0, type=int) 
    parser.add_argument('--epochs', default=2, type=int) 
    parser.add_argument('--batch_size', default=4, type=int) 
    parser.add_argument('--data_size', default=16, type=int) 
    parser.add_argument('--acc_steps', default=3, type=int) 
    parser.add_argument('--verbose', action='store_true',)

    args = parser.parse_args()

    print(args)

    main(args)

现在,如果我们运行这两个脚本:

  • python3 ddp.py — epochs 2 — batch_size 4 — data_size 8 — verbose — acc_steps 2

  • torchrun — standalone — nproc_per_node=2 ddp.py — epochs 2 — distributed — batch_size 4 — data_size 8 — verbose

我们会看到获得完全相同的最终模型参数:

# From Gradient Accumulator
Model weights :  Parameter containing:
tensor([[0.9472, 0.9440, 0.9527, 0.9687, 0.9570, 0.9343, 0.9411, 0.9186]],
       device='cuda:0', requires_grad=True)

# From DDP:
Model weights :  Parameter containing:
tensor([[0.9472, 0.9440, 0.9527, 0.9687, 0.9570, 0.9343, 0.9411, 0.9186]],
       device='cuda:0', requires_grad=True)

结论

在这篇文章中,我们简要介绍了 DP、DDP 算法以及梯度累积的基本概念,并展示了如何在没有多个 GPU 的情况下增加有效批量大小。需要注意的是,即使我们获得了相同的最终结果,使用多个 GPU 的训练速度也远快于使用梯度累积,因此如果训练速度很重要,那么多个 GPU 是加速训练的唯一途径。

结构方程模型中的多组分析

原文:towardsdatascience.com/multiple-group-analysis-in-structural-equation-modeling-2b2c7eef329?source=collection_archive---------7-----------------------#2023-06-16

测试子群体之间的效应

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

·

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

多组分析(MGA)是一种统计技术,允许研究人员通过指定具有组特定估计值或在各组之间具有相等估计值的结构方程模型(SEMs),来研究子群体或人口统计细分中的差异。

可以使用 MGA 来研究均值、回归、载荷、方差和变量的协方差之间的差异,因为所有这些参数都可以在 SEM 中建模。因此,即使其他建模技术(例如方差分析或带有交互效应的回归)也可以调查分组变量的作用,但这些技术在 SEM 中的灵活性不如 MGA。

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

图 1. 多组分析的一般概述以及推断策略。图像由作者提供。

多组分析的常见用途

每当有兴趣探索组间差异时,MGA 可以成为一个有用的工具。当数据是关于个人时,组通常基于具有少量层级的因素(例如,性别、民族、职业、家庭状态、健康状态等)来定义,但也可以根据不同的领域、数据和分析背景来定义其他各种因素。以下是一些可以在几个不同领域使用 MGA 回答的问题示例:

消费者研究

  • 满意度(或质量)在不同的人口统计群体中是否有所不同?

人力分析

  • 员工表现(或动机)在公司各分支机构或部门之间是否相等?

医疗保健

  • 患者报告的结果是否因药物制造商而异?

营销

  • 新的营销活动在不同地理区域提高品牌声誉的效果如何?

心理学

  • 情感体验是否存在跨文化差异?

教育

  • 学业成就的增长在女性和男性之间是否相等?

在多个组中测量未观察到的变量

上述所有问题涉及的变量都是未观察到的(例如,满意度、表现等),也称为潜在变量。由于这些变量无法直接观察,因此测量起来比较困难。

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

图 2. 比较未观察(潜在)变量与观察变量的测量。图像由作者提供。

这样一个困难是,不同的组可能对这些变量有不同的概念化。问问自己:

什么满意度?

什么良好的表现?

你的回答是否可能与那些具有不同生活经历的人的回答不同?

很多时候,答案是肯定的。

幸运的是,我们可以通过实证测试不同组是否以相似的方式概念化潜在变量。这个测试在 SEM 框架下通过 MGA 进行,称为因子不变性(也称为测量不变性)。因子不变性测试对于确保跨组比较的有效性至关重要;因此,如果存在潜在变量,这些测试必须在比较组间的回归或均值(即结构参数)之前进行。

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

图 3. 模型化未观察变量的挑战在于,它们可能在子群体之间测量的内容不相同。图片由作者提供。

参数差异测试

为了测试组间参数的差异,研究人员通常会拟合有无组间等式约束的结构方程模型。然后,使用似然比检验(等效于卡方差异检验)和其他拟合统计量(例如比较拟合指数和均方根误差)比较这两个模型,以评估施加约束是否导致模型拟合的统计学显著恶化。如果模型的拟合没有显著恶化,则保留具有等式约束的模型,并得出结论认为考虑的总体在测试的参数上没有显著差异。相反,如果模型的拟合显著恶化,则保留没有约束的模型(即允许每个组具有自己的估计),并得出结论认为考虑的总体在测试的参数上存在显著差异。

下图展示了在进行简单线性回归的双组示例中 MGA 背后的策略。此图显示了对一个参数施加的等式约束。模型 1 具有零自由度(即,完全饱和),而模型 2 由于等式约束而具有一个自由度。这些模型通过它们的卡方差异进行比较,该差异也服从自由度为一的卡方分布(模型之间的自由度差异)。可以通过对多个参数同时施加等式约束来进行更不特定的测试。

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

图 4. 具有简单线性回归的双组示例中 MGA 背后的策略。图片由作者提供。

结构方程模型(SEMs)作为确认性模型开发而来。 也就是说,人们提出假设,将其转化为可检验的统计模型,然后通过推断来确定数据是否支持这些假设。这种方法也适用于 MGA,并且对于避免大规模的 I 型错误率至关重要,这种错误会导致发现数据中实际上不存在的统计效应。因此,不推荐进行所有可能的组间比较。

MGA 估计的直观理解

免责声明: 以下段落适用于希望深入了解 MGA 的方法论者。本节假设读者了解全信息最大似然估计器。此外,这里列出的步骤仅用于解释 MGA 背后的逻辑。实际上,按照这些步骤进行 MGA 会低效,因为统计软件应利用简化此过程的算法。

MGA 的估计与具有缺失数据的简单 SEM 没有不同。在 MGA-SEM 的标准实施中,用户提交他们想要分析的数据及一个分组变量,该变量指示每个观察值所属的组。需要一个简单的数据处理步骤——使用分组变量——来为多个组设置分析。下面的图示出了用于分析的数据和用于 MGA 的数据重组。

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

图 5. 用户输入的数据与进行多组分析后的数据重组。图像由作者提供。

现在可以使用重组数据与完全信息最大似然法作为估计量,确保数据中的所有行都提交用于分析,即使存在缺失数据。重组数据的一些方便结果包括:

  • 任何给定行的对数似然仅受非缺失单元格的影响,因此将所有‘Group 0’行的对数似然相加会得到该组的对数似然。同样,将所有‘Group 1’行的对数似然相加会得到组 1 的对数似然。每组的对数似然用于估计整体模型的卡方统计量,该统计量量化了每组的拟合度。

  • 缺失值的模式禁止在各组变量间估计任何参数(例如,Var1_0 和 Var1_1 的协方差无法估计),这并不重要,因为 MGA 关注的是跨组效果的比较,而不是组间估计。

  • ‘Vanilla SEM’ 允许对参数设置等式约束。因此,使用重组后的数据在 SEM 中,可以指定两个相同的模型,每个模型的变量子集不同,并且可以对各组间等效参数设置等式约束。再重复一遍,所有这些都可以在标准 SEM 中完成,而无需明确要求软件进行 MGA。

幸运的是,想进行 MGA-SEM 的用户无需执行这些步骤!SEM 软件通过允许用户指定分组变量,使拟合多组模型变得非常简单。然而,进行数据处理(见图 5)并使用标准 SEM 进行 MGA-SEM 将加深你对这个主题的理解。要了解更多信息,请查看以下引用的资源。

JMP 中应用的多组分析的逐步示例

有关因子(测量)不变性的多组分析的章节

Widaman, K. F., & Olivera-Aguilar, M. (2022). 使用确认性因素分析调查测量不变性。结构方程建模手册, 367.

期刊文章 关于使用替代适配度指数测试不变性:

Chen, F. F. (2007). 对适配度指数对测量不变性的敏感性。结构方程建模:多学科期刊14(3),464–504。

本文最初发表于 JMP 用户社区 ,发布时间为 2023 年 2 月 27 日。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值