PyTorch 中 coalesce() 函数详解与应用示例

PyTorch 中 coalesce() 函数详解与应用示例

coalesce: 美 [ˌkoʊəˈlɛs] 合并;凝聚;联结,注意发音

引言

在 PyTorch 中,稀疏张量(Sparse Tensor)是一种高效存储和操作稀疏数据的方式。稀疏张量主要用于需要处理大量零元素的场景,例如图神经网络(GNN)和大型矩阵操作。本文将深入解析 coalesce() 函数的用法,并结合代码示例进行演示。

coalesce() 函数简介

coalesce() 是 PyTorch 稀疏张量的一个成员函数,主要用于去重和合并重复索引的元素。在稀疏张量中,可能存在重复的坐标位置,coalesce() 可以将这些重复的坐标进行合并,并对相同索引的值进行累加。

语法

sparse_tensor.coalesce()

功能

  • 去重合并索引:将具有重复索引的元素合并为一个,值相加。
  • 输出稀疏张量:返回新的 coalesced 稀疏张量,减少存储开销并优化计算效率。

使用示例

基本示例

import torch

# 创建一个稀疏张量
indices = torch.tensor([[0, 1, 1], [2, 0, 0]])  # 表示坐标
values = torch.tensor([3.0, 4.0, 5.0])        # 对应值
sparse_tensor = torch.sparse_coo_tensor(indices, values, (2, 3))

print("未合并之前:")
print(sparse_tensor)

# 使用 coalesce() 合并重复索引
coalesced_tensor = sparse_tensor.coalesce()

print("合并之后:")
print(coalesced_tensor)

输出结果:

未合并之前:
tensor(indices=tensor([[0, 1, 1],
                       [2, 0, 0]]),
       values=tensor([3., 4., 5.]),
       size=(2, 3), nnz=3, layout=torch.sparse_coo)
合并之后:
tensor(indices=tensor([[0, 1],
                       [2, 0]]),
       values=tensor([3., 9.]),
       size=(2, 3), nnz=2, layout=torch.sparse_coo)

可以看到,坐标 [1, 0] 重复出现了两次,值 4.05.0 被合并成了 9.0

这个例子的具体解析如下

在这个例子中,indicesvalues 用来创建一个稀疏张量,表示了一个 2x3 的张量。下面逐步解释如何合并重复的索引,并解释每个元素的含义。

初始稀疏张量:

indices = torch.tensor([[0, 1, 1], [2, 0, 0]])  
values = torch.tensor([3.0, 4.0, 5.0])

indices 表示张量中非零元素的坐标,每一列表示一个非零元素的坐标:

  • 第一列 [0, 2] 代表位置 (0, 2),即第一行第三列。
  • 第二列 [1, 0] 代表位置 (1, 0),即第二行第一列。
  • 第三列 [1, 0] 代表位置 (1, 0),即第二行第一列。

values 是这些坐标位置对应的值:

  • 位置 (0, 2) 的值是 3.0。
  • 位置 (1, 0) 的值是 4.0。
  • 位置 (1, 0) 的值是 5.0。

因此,张量的稀疏表示为:

[[0, 0, 3.0],
 [4.0 + 5.0, 0, 0]]

也就是说,第二行的第一列包含两个非零元素:4.0 和 5.0。

使用 coalesce() 合并重复索引:

coalesced_tensor = sparse_tensor.coalesce()

coalesce() 会合并重复的索引,并将它们的值相加。具体地:

  • 对于位置 (1, 0),它有两个非零值 4.0 和 5.0,因此它们会被合并为 9.0。
  • 位置 (0, 2) 保持为 3.0,因为它是唯一的非零元素。

所以,合并之后的张量变为:

[[0, 0, 3.0],
 [9.0, 0, 0]]

合并后的稀疏张量:

tensor(indices=tensor([[0, 1],
                       [2, 0]]),
       values=tensor([3., 9.]),
       size=(2, 3), nnz=2, layout=torch.sparse_coo)
  • indices 表示合并后的非零元素的坐标。只有两个非零元素:一个位于 (0, 2),另一个位于 (1, 0)。
  • values 表示这些坐标位置的值:3.0 和 9.0。
  • nnz=2 表示非零元素的数量(合并后的张量有两个非零元素)。

总结:
合并后的稀疏张量仍然是一个 2x3 张量,但是它去掉了重复的索引,并将重复索引位置的值进行了求和。

用于 FP16 梯度处理

在混合精度训练(AMP)中,如果需要处理 FP16 精度的稀疏梯度,可能会遇到重复索引问题。例如,在分布式训练中累积梯度时,需要确保梯度不会因为重复索引而导致计算错误。因此,我们可以利用 coalesce() 来合并梯度。

以下是一个示例代码片段:

import torch

# 模拟 FP16 的稀疏梯度
indices = torch.tensor([[0, 1, 1], [2, 0, 0]])
values = torch.tensor([0.5, 0.25, 0.25], dtype=torch.float16)
grad = torch.sparse_coo_tensor(indices, values, (2, 3), dtype=torch.float16)

# 合并梯度
if grad.dtype == torch.float16:
    grad = grad.coalesce()

print(grad)

输出结果:

tensor(indices=tensor([[0, 1],
                       [2, 0]]),
       values=tensor([0.5000, 0.5000], dtype=torch.float16),
       size=(2, 3), nnz=2, layout=torch.sparse_coo)

在 AMP 训练过程中,这种操作确保梯度不会因为浮点精度或重复索引导致数值错误。

高级用法

1. 检查是否已合并

可以使用 is_coalesced() 函数检查稀疏张量是否已合并:

if not grad.is_coalesced():
    grad = grad.coalesce()

2. 支持更高维度操作

coalesce() 也支持高维稀疏张量。例如:

indices = torch.tensor([[0, 1, 1, 2], [2, 0, 0, 1], [1, 2, 2, 3]])
values = torch.tensor([1.0, 2.0, 3.0, 4.0])
sparse_tensor = torch.sparse_coo_tensor(indices, values, (3, 3, 4))
coalesced_tensor = sparse_tensor.coalesce()
print(coalesced_tensor)

Output

tensor(indices=tensor([[0, 1, 2],
                       [2, 0, 1],
                       [1, 2, 3]]),
       values=tensor([1., 5., 4.]),
       size=(3, 3, 4), nnz=3, layout=torch.sparse_coo)
分析这个稀疏张量的情况

稀疏张量的构建与合并过程:
indices 解析:

indices 是一个 3x4 的张量,表示四个非零元素的坐标。每一列代表一个坐标,分别是:

  • 第一列 [0, 2, 1],表示位置 (0, 2, 1),即第一维索引 0,第二维索引 2,第三维索引 1。
  • 第二列 [1, 0, 2],表示位置 (1, 0, 2),即第一维索引 1,第二维索引 0,第三维索引 2。
  • 第三列 [1, 0, 2],表示位置 (1, 0, 2),即第一维索引 1,第二维索引 0,第三维索引 2。
  • 第四列 [2, 1, 3],表示位置 (2, 1, 3),即第一维索引 2,第二维索引 1,第三维索引 3。

values 解析:

values 是一个长度为 4 的张量,表示在上述位置的值:

  • 位置 (0, 2, 1) 的值为 1.0。
  • 位置 (1, 0, 2) 的值为 2.0。
  • 位置 (1, 0, 2) 的值为 3.0。
  • 位置 (2, 1, 3) 的值为 4.0。

创建的稀疏张量:

稀疏张量的维度为 (3, 3, 4),并且非零元素位于:

  • 位置 (0, 2, 1) 为 1.0。
  • 位置 (1, 0, 2) 为 2.0。
  • 位置 (1, 0, 2) 为 3.0(同样位置的两个值合并)。
  • 位置 (2, 1, 3) 为 4.0。

使用 coalesce() 合并重复索引:

当调用 coalesce() 时,它会合并相同坐标上的值,将它们相加。因此,位置 (1, 0, 2) 的值 2.0 和 3.0 会合并为 5.0。

合并后的非零位置和值:

  • 位置 (0, 2, 1) 的值为 1.0。
  • 位置 (1, 0, 2) 的值为 5.0(2.0 + 3.0)。
  • 位置 (2, 1, 3) 的值为 4.0。

输出整个张量:

现在我们可以构造一个完整的张量,其中非零位置的值已经被填充,而其他位置仍然是零。结果应为:

[[[ 0, 0, 0, 0],
  [ 0, 0, 0, 0],
  [ 0, 1, 0, 0]],

 [[ 0, 0, 5, 0],
  [ 0, 0, 0, 0],
  [ 0, 0, 0, 0]],

 [[ 0, 0, 0, 0],
  [ 0, 0, 0, 4],
  [ 0, 0, 0, 0]]]

解释:

  • 位置 (0, 2, 1) 的值是 1.0。
  • 位置 (1, 0, 2) 的值是 5.0(合并了 2.0 和 3.0)。
  • 位置 (2, 1, 3) 的值是 4.0。
  • 其他位置都为零。

因此,最终的输出张量是:

[[[ 0, 0, 0, 0],
  [ 0, 0, 0, 0],
  [ 0, 1, 0, 0]],

 [[ 0, 0, 5, 0],
  [ 0, 0, 0, 0],
  [ 0, 0, 0, 0]],

 [[ 0, 0, 0, 0],
  [ 0, 0, 0, 4],
  [ 0, 0, 0, 0]]]

这个结果就是在合并重复索引后得到的稀疏张量。

3. 在优化器中的应用

在梯度缩放优化器中,我们可以利用 coalesce() 保持梯度一致性:

for param in model.parameters():
    if param.grad is not None and param.grad.is_sparse:
        param.grad = param.grad.coalesce()
具体解释

在优化器中的应用中,coalesce() 的作用是在稀疏梯度(例如,在使用稀疏参数或稀疏更新的模型时)中合并相同位置上的梯度,以确保梯度的一致性。特别是在分布式训练或使用梯度累积时,多个梯度更新可能会产生相同位置上的不同梯度值。在这种情况下,调用 coalesce() 可以将相同位置的梯度值合并(加和),防止在更新参数时重复计算,从而提高训练效率并避免梯度值不一致。

具体应用场景:
在训练过程中,当某些参数的梯度是稀疏的(即只有少数位置的梯度非零),如果在多个阶段对这些位置进行更新,可能会出现多个梯度值对同一位置进行计算。此时,我们希望对相同位置的梯度值进行合并,以保证这些位置的最终梯度是正确的。这通常发生在使用稀疏矩阵操作时(如稀疏优化器、稀疏神经网络)。

通过调用 coalesce(),我们可以确保相同位置的梯度值加和到一起,避免因为重复计算导致的不一致问题。

数值模拟:
假设我们有一个包含两个参数的模型,且它们的梯度是稀疏的。我们将模拟以下场景:

  1. 初始稀疏梯度:设定一个稀疏梯度,并模拟两个步骤的梯度更新。
  2. 梯度合并前:在两个步骤中,我们对相同位置的参数更新了不同的梯度值。
  3. 梯度合并后:使用 coalesce() 合并相同位置的梯度。
import torch

# 假设模型有2个参数,每个参数的梯度是稀疏的
# 初始梯度
indices = torch.tensor([[0, 1], [1, 0]])  # 表示在位置 (0, 1) 和 (1, 0) 上有梯度
values = torch.tensor([1.0, 2.0])  # 对应位置的梯度
sparse_grad = torch.sparse_coo_tensor(indices, values, (2, 2))

# 输出初始稀疏梯度
print("初始稀疏梯度:")
print(sparse_grad)

# 模拟第二次更新,更新相同位置
indices_new = torch.tensor([[0, 1], [1, 0]])  # 仍然是相同的位置
values_new = torch.tensor([3.0, 4.0])  # 对应位置新的梯度
sparse_grad_new = torch.sparse_coo_tensor(indices_new, values_new, (2, 2))

# 合并梯度
combined_grad = sparse_grad + sparse_grad_new  # 两个稀疏梯度相加

# 合并后的稀疏梯度
print("\n合并前的稀疏梯度:")
print(combined_grad)

# 使用 coalesce() 合并相同位置的梯度
coalesced_grad = combined_grad.coalesce()

# 合并后的稀疏梯度
print("\n合并后的稀疏梯度:")
print(coalesced_grad)

解释:

  1. 初始梯度
    我们定义了一个稀疏梯度 sparse_grad,它在位置 (0, 1)(1, 0) 具有梯度值 1.0 和 2.0。

  2. 第二次更新
    模拟了一个新的稀疏梯度 sparse_grad_new,其中位置 (0, 1)(1, 0) 的梯度分别更新为 3.0 和 4.0。

  3. 合并梯度
    在合并两个稀疏梯度时,直接将它们相加,得到 combined_grad。此时,两个位置上的梯度被简单地加在一起,得到的梯度分别是 4.0(1.0 + 3.0)和 6.0(2.0 + 4.0)。

  4. 梯度合并(coalesce)
    使用 coalesce() 方法后,任何相同位置的梯度会被合并。由于我们的梯度已经在上一步合并,coalesce() 会确保这些位置的梯度值是正确的。如果存在重复的索引,coalesce() 会将它们的值加和。

输出:

初始稀疏梯度:
tensor(indices=tensor([[0, 1],
                       [1, 0]]),
       values=tensor([1., 2.]),
       size=(2, 2), nnz=2, layout=torch.sparse_coo)

合并前的稀疏梯度:
tensor(indices=tensor([[0, 1],
                       [1, 0]]),
       values=tensor([4., 6.]),
       size=(2, 2), nnz=2, layout=torch.sparse_coo)

合并后的稀疏梯度:
tensor(indices=tensor([[0, 1],
                       [1, 0]]),
       values=tensor([4., 6.]),
       size=(2, 2), nnz=2, layout=torch.sparse_coo)

结论:

  1. 合并前:我们看到 combined_grad 中相同位置的梯度已经进行了加法操作,但这只是简单的相加,并没有执行任何额外的合并操作。
  2. 合并后:由于 coalesce() 会将相同位置的梯度加和,这个操作确保了梯度在同一位置的值是一致的,避免了重复的更新。

通过这种方式,coalesce() 可以有效地帮助我们在稀疏梯度中保持一致性,确保在更新模型参数时不会出现梯度冲突或不一致的情况。

注意事项

  1. 自动合并限制:某些 PyTorch 操作可能不会自动对稀疏张量进行合并,因此需要手动调用 coalesce()
  2. 内存优化:在大规模稀疏矩阵计算中,合并操作有助于减少内存开销,提高计算效率。
  3. 不可逆操作coalesce() 会生成新的张量,如果需要保留原始数据,需提前备份。

结论

coalesce() 是处理稀疏张量中重复索引的重要工具,尤其适合需要处理混合精度训练的梯度更新场景。通过上述示例和应用场景,希望读者对该函数有更深入的理解,并能在实际项目中灵活应用。

后记

2025年1月2日19点29分于上海,在GPT4o mini的辅助下完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/947566.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RWKV 语言模型

RWKV Language Model是一种独特的循环神经网络(RNN)架构的语言模型,具有诸多优势和特点,在自然语言处理领域展现出了良好的性能和应用潜力,以下是具体介绍: 核心原理 融合RNN与Transformer优点:…

基于单片机的温湿度采集系统(论文+源码)

2.1系统的功能 本系统的研制主要包括以下几项功能: (1)温度检测功能:对所处环境的温度进行检测; (2)湿度检测功能:对所处环境的湿度进行检测; (3)加热和制冷功能:可以完成加热和制冷功能。 (4)加湿和除…

「Mac畅玩鸿蒙与硬件49」UI互动应用篇26 - 数字填色游戏

本篇教程将带你实现一个数字填色小游戏,通过简单的交互逻辑,学习如何使用鸿蒙开发组件创建趣味性强的应用。 关键词 UI互动应用数字填色动态交互逻辑判断游戏开发 一、功能说明 数字填色小游戏包含以下功能: 数字选择:用户点击…

OCR图片中文字识别(Tess4j)

文章目录 Tess4J下载 tessdataJava 使用Tess4j 的 demo Tess4J Tess4J 是 Tesseract OCR 引擎的 Java 封装库,它让 Java 项目更轻松地实现 OCR(光学字符识别)功能。 下载 tessdata 下载地址:https://github.com/tesseract-ocr/…

Redis面试相关

Redis开篇 使用场景 缓存 缓存穿透 解决方法一: 方法二: 通过多次hash来获取对应的值。 小结 缓存击穿 缓存雪崩 打油诗 双写一致性 两种不同的要求 强一致 读锁代码 写锁代码 强一致,性能低。 延迟一致 方案一:消息队列 方…

【快速实践】深度学习 -- 数据曲线平滑化

希望对你有帮助呀!!💜💜 如有更好理解的思路,欢迎大家留言补充 ~ 一起加油叭 💦 欢迎关注、订阅专栏 【深度学习从 0 到 1】谢谢你的支持! 在观察数据结果时,我们通常希望获得整体趋…

RS485方向自动控制电路分享

我们都知道RS485是半双工通信,所以在传输的时候需要有使能信号,标明是发送还是接收信号,很多时候就简单的用一个IO口控制就好了,但是有一些低成本紧凑型的MCU上,一个IO口也是很珍贵的,因此,如果…

DevSecOps自动化在安全关键型软件开发中的实践、Helix QAC Klocwork等SAST工具应用

DevSecOps自动化对于安全关键型软件开发至关重要。 那么,什么是DevSecOps自动化?具有哪些优势?为何助力安全关键型软件开发?让我们一起来深入了解~ 什么是DevSecOps自动化? DevSecOps自动化是指在软件开发生命周期的各…

【ArcGISPro/GeoScenePro】解决常见的空间参考和投影问题

修复空间参考缺失的图像 数据 https://arcgis.com/sharing/rest/content/items/535efce0e3a04c8790ed7cc7ea96d02d/data 查看属性坐标 查看属性范围 范围值并不是零或接近于零。 这意味着栅格具有范围,因此其已正确进行

十二、Vue 路由

文章目录 一、简介二、安装与基本配置安装 Vue Router创建路由实例在应用中使用路由实例三、路由组件与视图路由组件的定义与使用四、动态路由动态路由参数的定义与获取动态路由的应用场景五、嵌套路由嵌套路由的概念与配置嵌套路由的应用场景六、路由导航<router - link>…

C#实现画图,及实现图像运动,C#中GDI+图形图像技术(Graphics类、Pen类、Brush类)C#之快速入门GDI+绘图 C#实现快速画图功能

下载源码 <-------- 在C#的世界里&#xff0c;GDI如同一位多才多艺的艺术家&#xff0c;以其强大的绘图能力&#xff0c;让开发者能够轻松地在应用程序中挥洒创意&#xff0c;绘制出丰富多彩的图形世界。GDI不仅支持基本的几何图形绘制&#xff0c;还能处理复杂的图像处理任…

Echart实现3D饼图示例

在可视化项目中&#xff0c;很多地方会遇见图表&#xff1b;echart是最常见的&#xff1b;这个示例就是用Echart&#xff0c; echart-gl实现3D饼图效果&#xff0c;复制即可用 //需要安装&#xff0c;再引用依赖import * as echarts from "echarts"; import echar…

Devart dotConnect发布全新版本,支持EF Core 9、完全兼容 .NET 9 等!

dotConnect &#xff08;最新版dotConnect Universal试用下载&#xff09;是Devart旗下一种基于 ADO.NET 架构构建的增强型数据连接解决方案&#xff0c;也是一个采用多项创新技术的开发框架。dotConnect 包含面向主要数据库和流行云应用程序的高性能数据提供程序&#xff0c;并…

借助 FinClip 跨端技术探索鸿蒙原生应用开发之旅

在当今数字化浪潮汹涌澎湃的时代&#xff0c;移动应用开发领域正经历着深刻的变革与创新。鸿蒙操作系统的崛起&#xff0c;以其独特的分布式架构和强大的性能表现&#xff0c;吸引了众多开发者的目光。而FinClip 跨端技术的出现&#xff0c;为开发者涉足鸿蒙原生应用开发提供了…

UE5.3 虚幻引擎 Windows插件开发打包(带源码插件打包、无源码插件打包)

0 引言 随着项目体量的增大&#xff0c;所有代码功能都放一起很难管理。所以有什么办法可以将大模块划分成一个个小模块吗。当然有&#xff0c;因为虚幻引擎本身就遇到过这个问题&#xff0c;他的解决办法就是使用插件的形式开发。 例如&#xff0c;一个团队开发了文件I/O模块插…

如何轻松关闭 iPhone 上的 HEIC [HEIC 图像技巧]

您是否正在为关闭 iPhone 上的 HEIC 而烦恼&#xff1f;你不是一个人; Apple 的首选图像文件格式仍可能存在一些兼容性问题。当您与某人共享照片或尝试在Windows计算机上打开图像时&#xff0c;就会出现此问题。幸运的是&#xff0c;Apple 使关闭 HEIC iPhone 变得更加容易。 …

GRU-PFG:利用图神经网络从股票因子中提取股票间相关性

“MCI-GRU: Stock Prediction Model Based on Multi-Head Cross-Attention and Improved GRU” 论文地址&#xff1a;https://arxiv.org/pdf/2410.20679 摘要 金融市场因复杂性及大数据时代的来临&#xff0c;使得准确预测股票走势变得尤为重要。传统的时序分析模型&#xff0…

UE5失真材质

渐变材质函数&#xff1a;RadialGradientExponential&#xff08;指数径向渐变&#xff09; 函数使用 UV 通道 0 来产生径向渐变&#xff0c;同时允许用户调整半径和中心点偏移。 用于控制渐变所在的位置及其涵盖 0-1 空间的程度。 基于 0-1 的渐变中心位置偏移。 源自中心的径…

Go语言在实际项目中的应用:从RESTful API到日志监控 (十四)

Go语言在实际项目中的应用&#xff1a;从RESTful API到日志监控 &#x1f680; Go语言&#xff08;又叫Golang&#xff09;作为一种现代化的编程语言&#xff0c;凭借其简洁的语法和强大的性能&#xff0c;已经成为了很多企业技术栈的一部分。在实际项目中&#xff0c;Go不仅仅…

3blue1brow线代笔记

向量 物理&#xff1a;空间中的箭头&#xff0c;长度和方向决定一个向量。只要两者相同&#xff0c;可以任意移动保持不变 计算机&#xff1a;有序的数字列表 &#xff08;数组&#xff09; 数学&#xff1a;向量可以是任何东西&#xff0c;只要保证两个向量相加以及数字与向量…