PyTorch团队重写-比原始实现快八倍-分割一切-模型 (pytorch)
简介
近年来,生成式 AI 经历了飞速发展,但训练和推理这些模型仍然面临着挑战,特别是在使用 PyTorch 时。本文介绍了一种解决方案,重点介绍如何利用原生 PyTorch 特性加速生成式 AI 模型。PyTorch 原生特性
PyTorch 近期开发了新功能,可以显著提高吞吐量并减少内存开销: torch.compile:静态编译并优化 PyTorch 模型,提高执行速度。 SDPA:深度可分离卷积,减少内存占用。 Tritonkernels:NVIDIA 的优化内核库,提高性能。 NestedTensor:一种新的数据结构,可以高效地表示嵌套数据。 半结构化稀疏:一种稀疏表示,可以进一步减少内存占用。优化分割一切模型
为了展示这些功能的实际应用,本文通过优化 Meta 的分割一切 (SAM) 模型来重写。SAM 是一种基于生成式对抗网络 (GAN) 的图像分割模型。性能分析
使用 PyTorch Profiler 分析 SAM,确定了两个主要的瓶颈: 索引操作:`aten::index` 函数的频繁调用导致 GPU 同步。 矩阵乘法:Transformers 中常见的昂贵操作。优化流程
使用 PyTorch 新功能优化 SAM: 1. 使用 Bfloat16 半精度 降低精度以减少计算时间和内存。 2. 移除 GPU 同步 使用 `torch.where` 重写索引操作,避免 GPU 复制。 3. 应用 SDPA 优化卷积运算,减少内存消耗。 4. 集成 Tritonkernels 利用 NVIDIA 优化的内核库提高性能。 5. 使用 NestedTensor 高效表示嵌套数据,进一步优化内存使用。结果
优化后的 SAM 具有以下性能提升: 执行速度提高 8 倍 内存占用减少 50%快速版 SAM
本文还提供了快速版 SAM,作为 PyTorch 实验室的一部分。该版本结合了上述优化,并提供了一个易于使用的 API。可视化性能
使用 PerfettoUI 可视化优化效果,清楚地展示了 PyTorch 每个特性的应用价值。结论
通过原生 PyTorch 特性,可以显著加速生成式 AI 模型。本文优化了 SAM 模型,展示了如何组合这些特性以提高性能。优化后的 SAM 代码已开源,提供了一个快速且高效的图像分割模型。资源
[PyTorch 博客文章]([SAM 优化代码]([PerfettoUI 可视化](PyTorch生成3D模型
本文将介绍如何利用深度学习技术生成3D模型,使用了PyTorch和PolyGen。
有一个新兴的深度学习研究领域专注于将 DL 技术应用于 3D 几何和计算机图形应用程序,这一长期研究的集合证明了这一点。对于希望自己尝试一些 3D 深度学习的 PyTorch 用户,Kaolin 库值得研究。对于 TensorFlow 用户,还有TensorFlow Graphics。一个特别热门的子领域是 3D 模型的生成。创造性地组合 3D 模型、从图像快速生成 3D 模型以及为其他机器学习应用程序和模拟创建合成数据只是 3D 模型生成的无数用例中的一小部分。
然而,在 3D 深度学习研究领域,为你的数据选择合适的表示是成功的一半。在计算机视觉中,数据的结构非常简单:图像由密集的像素组成,这些像素整齐均匀地排列成精确的网格。3D 数据的世界没有这种一致性。3D 模型可以表示为体素、点云、网格、多视图图像集等。这些输入表示也都有自己的一组缺点。例如,体素尽管计算成本很高,但输出分辨率很低。点云不编码表面或其法线的概念,因此不能仅从点云唯一地推断出拓扑。网格也不对拓扑进行唯一编码,因为任何网格都可以细分以产生相似的表面。PolyGen,一种用于网格的神经生成模型,它联合估计模型的面和顶点以直接生成网格。DeepMind GitHub 上提供了官方实现。
现在经典的PointNet论文为点云数据建模提供了蓝图,例如 3D 模型的顶点。它是一种通用算法,不对 3D 模型的面或占用进行建模,因此无法单独使用 PointNet 生成独特的防水网格。3D-R2N2采用的体素方法将我们都熟悉的 2D 卷积扩展到 3D,并自然地从 RGB 图像生成防水网格。然而,在更高的空间分辨率下,体素表示的计算成本很高,有效地限制了它可以产生的网格的大小。
Pixel2Mesh可以通过变形模板网格(通常是椭圆体)从单个图像预测 3D 模型的顶点和面。目标模型必须与模板网格同胚,因此使用凸模板网格(例如椭圆体)会在椅子和灯等高度非凸的物体上引入许多假面。拓扑修改网络(TMN) 通过引入两个新阶段在 Pixel2Mesh 上进行迭代:拓扑修改阶段用于修剪会增加模型重建误差的错误面,以及边界细化阶段以平滑由面修剪引入的锯齿状边界。如果你有兴趣,我强烈建议同时查看AtlasNet和Hierarchical Surface Prediction。
虽然变形和细化模板网格的常用方法表现良好,但它始于对模型拓扑的主要假设。就其核心而言,3D 模型只是 3D 空间中的一组顶点,通过各个面进行分组和连接在一起。是否可以避开中间表示并直接预测这些顶点和面?
PolyGen 通过将 3D 模型表示为顶点和面的严格排序序列,而不是图像、体素或点云,对模型生成任务采取了一种相当独特的方法。这种严格的排序使他们能够应用基于注意力的序列建模方法来生成 3D 网格,就像 BERT 或 GPT 模型对文本所做的那样。
PolyGen 的总体目标有两个:首先为 3D 模型生成一组合理的顶点(可能以图像、体素或类标签为条件),然后生成一系列面,一个接一个,连接顶点在一起,并为此模型提供一个合理的表面。组合模型将网格上的分布 表示为两个模型之间的联合分布:顶点模型 表示顶点,面模型 表示以顶点为条件的面。
顶点模型是一个解码器,它试图预测以先前标记为条件的序列中的下一个标记(并且可选地以图像、体素字段或类标签为条件)。人脸模型由一个编码器和一个解码器指针网络组成,该网络表示顶点序列上的分布。该指针网络一次有效地“选择”一个顶点,以添加到当前面序列并构建模型的面。该模型以先前的人脸序列和整个顶点序列为条件。由于 PolyGen 架构相当复杂并且依赖于各种概念,因此本文将仅限于顶点模型。
流行的ShapeNetCore数据集中的每个模型都可以表示为顶点和面的集合。每个顶点由一个 (x, y, z) 坐标组成,该坐标描述了 3D 网格中的一个点。每个面都是一个索引列表,指向构成该面角的顶点。对于三角形面,此列表长 3 个索引。对于 n 边形面,此列表是可变长度的。原始数据集非常大,因此为了节省时间,我在此处提供了一个更轻量级的预处理数据集子集供你进行实验。该子集仅包含来自 5 个形状类别的模型,并且在转换为 n 边形后少于 800 个顶点(如下所述)。
为了使序列建模方法发挥作用,数据必须以一种受约束的、确定性的方式表示,以尽可能多地消除可变性。出于这个原因,作者对数据集进行了一些简化。首先,他们将所有输入模型从三角形(连接 3 个顶点的面)转换为 n 边形(连接 n 个顶点的面),使用Blender 的平面抽取修改器合并面。这为相同的拓扑提供了更紧凑的表示,并减少了三角剖分中的歧义,因为大型网格并不总是具有唯一的三角剖分。为了篇幅的缘故,我不会在这篇文章中讨论 Blender 脚本,但有很多资源,包括官方文档和GitHub 上的这套优秀示例,很好地涵盖了这个主题。我提供的数据集已经预先抽取。
要继续进行,请下载此示例 文件。这个模型是一个基本的立方体,有 8 个顶点和 6 个面。以下简单代码片段从单个 文件中读取所有顶点。
其次,顶点首先从它们的 z 轴(在这种情况下为垂直轴)按升序排序,然后是 y 轴,最后是 x 轴。这样,模型顶点是自下而上表示的。在 vanilla PolyGen 模型中,然后将顶点连接成一维序列向量,对于较大的模型,该向量最终会得到一个非常长的序列向量。作者在论文的附录 E 中描述了一些减轻这种负担的修改。
要对一系列顶点进行排序,我们可以使用字典排序。这与对字典中的单词进行排序时采用的方法相同。要对两个单词进行排序,您将查看第一个字母,然后如果有平局,则查看第二个字母,依此类推。对于“aardvark”和“apple”这两个词,第一个字母是“a”和“a”,所以我们移动到第二个字母“a”和“p”来告诉我“aardvark”在“apple”之前。在这种情况下,我们的“字母”是按顺序排列的 z、y 和 x 坐标。
最后,顶点坐标被归一化,然后被量化以将它们转换为离散的 8 位值。这种方法已在像素递归神经网络和WaveNet中用于对音频信号进行建模,使它们能够对顶点值施加分类分布。在最初的WaveNet论文中,作者评论说“分类分布更灵活,并且可以更容易地对任意分布进行建模,因为它不对它们的形状做任何假设。” 这种质量对于建模复杂的依赖关系很重要,例如 3D 模型中顶点之间的对称性。
顶点模型由一个解码器网络组成,它具有变压器模型的所有标准特征:输入嵌入、18 个变压器解码器层的堆栈、层归一化,最后是在所有可能的序列标记上表示的 softmax 分布。给定一个长度为 的扁平顶点序列 ,其目标是在给定模型参数的情况下最大化数据序列的对数似然:
与 LSTM 不同的是,transformer 模型能够以并行方式处理顺序输入,同时仍使来自序列一部分的信息能够为另一部分提供上下文。这一切都归功于他们的注意力模块。3D 模型的顶点包含各种对称性和远点之间的复杂依赖关系。例如,考虑一个典型的桌子,其中模型对角的腿是彼此的镜像版本。注意力模块允许对这些类型的模式进行建模。
嵌入层是序列建模中用于将有限数量的标记转换为特征集的常用技术。在语言模型中,“国家”和“民族”这两个词的含义可能非常相似,但与“苹果”这个词却相距甚远。当单词用唯一的标记表示时,就没有相似性或差异性的固有概念。嵌入层将这些标记转换为矢量表示,可以对有意义的距离感进行建模。
PolyGen 将同样的原理应用于顶点。该模型使用三种类型的嵌入层:坐标表示输入标记是 x、y 还是 z 坐标,值表示标记的值,以及位置编码顶点的顺序。每个都向模型传达有关令牌的一条信息。由于我们的顶点一次在一个轴上输入,坐标嵌入为模型提供了基本的坐标信息,让它知道给定值对应的坐标类型。
值嵌入对我们之前创建的量化顶点值进行编码。我们还需要一些序列控制点:额外的开始和停止标记分别标记序列的开始和结束,并将标记填充到最大序列长度。
由于并行化而丢失的给定序列位置 n的位置信息通过位置嵌入来恢复。 也可以使用位置编码,一种不需要学习的封闭形式的表达。在经典的 Transformer 论文“ Attention Is All You Need ”中,作者定义了一种由不同频率的正弦和余弦函数组成的位置编码。他们通过实验确定位置嵌入的性能与位置编码一样好,但编码的优势在于比训练中遇到的序列更长。有关位置编码的出色视觉解释,请查看此博客文章。
生成所有这些标记序列后,最后要做的是创建一些嵌入层并将它们组合起来。每个嵌入层都需要知道期望的输入字典的大小和输出的嵌入维度。每层的嵌入维数为 256,这意味着我们可以将它们与加法相结合。字典大小取决于输入可以具有的唯一值的数量。对于值嵌入,它是量化值的数量加上控制标记的数量。对于坐标嵌入,对于每个坐标 x、y 和 z,它是一个,对于上述任何一个(控制标记)都不是一个。最后,位置嵌入对于每个可能的位置或最大序列长度都需要一个。
PolyGen 还广泛使用无效预测掩码来确保其生成的顶点和面部序列编码有效的 3D 模型。例如,必须强制执行诸如“z 坐标不递减”和“停止标记只能出现在完整顶点(z、y 和 x 标记的三元组)之后”之类的规则,以防止模型产生无效的网格. 作者在论文的附录 F 中提供了他们使用的掩蔽的广泛列表。这些约束仅在预测时强制执行,因为它们实际上会损害训练性能。
与许多序列预测模型一样,该模型是自回归的,这意味着给定时间步的输出是下一个时间步的可能值的分布。整个序列一次预测一个标记,模型在每一步都会查看先前时间步骤中的所有标记以选择其下一个标记。解码策略决定了它如何从这个分布中选择下一个Token。
如果使用次优解码策略,生成模型有时会陷入重复循环或产生质量较差的序列。我们都看到生成的文本看起来像是胡说八道。PolyGen 采用称为 核采样 的解码策略来生成高质量序列。原始论文在文本生成上下文中应用了这种方法,但它也可以应用于顶点。前提很简单:仅从 softmax 分布中共享 top-p 概率质量的标记中随机抽取下一个标记。这在推理时应用以生成网格,同时避免序列退化。有关核采样的 PyTorch 实现,请参阅此要点。
除了无条件生成模型外,PolyGen 还支持使用类标签、图像和体素进行输入调节。这些可以指导生成具有特定类型、外观或形状的网格。类标签通过嵌入投影,然后添加到每个注意力块中的自注意力层之后。对于图像和体素,编码器创建一组嵌入,然后用于与转换器解码器的交叉注意。
PolyGen 模型描述了一个强大、高效和灵活的框架,用于有条件地生成 3D 网格。序列生成可以在各种条件和输入类型下完成,从图像到体素到简单的类标签,甚至只是一个起始标记。表示网格顶点分布的顶点模型只是联合分布难题的一部分。我打算在以后的文章中介绍面部模型。同时,我鼓励你查看DeepMind 的 TensorFlow 实现,并尝试生成条件模型!
原文链接:
如何用深度学习进行CT影像肺结节探测(附
1.数据预处理首先用SimpleITK把mhd图片读入,对每个切片使用Gaussian filter然后使用阈值-600把肺部图片二值化,然后再分析该切片的面积,去掉面积小于30mm2的区域和离心率大于0.99的区域,找到3D的连通区域。 只保留0.68L到8.2L体积的区域,并且如果大于6000 mm2的区域到切片的中心区域的距离大于62mm也删除该连通区。 最后只留下一个最大的连通区域。 左边是原始图,右边是切完肺的。 在实际中预处理中,我们可视化了每个肺的部分切片,存在一些bad case。 主要有以下3种,我们也对这3种情况做了优化:把肺边缘结节切掉。 因为阈值导致的,把二值化环境-600改成-150有改善。 切出来全部为黑的(未找到任何肺部区域)。 有些ct图是从头部开始扫描的,导致影响了连通区域判断,需要手动查看该mhd文件,看里面的从第个切片到第几个切片是肺部,在做完二值化操作后,人为把前面和后面的切片全部设置为0。 切出来只有一侧肺部情况。 有些患者两个肺的大小差别比较大,需要调整阈值,放宽阈值标注,把大于6000 mm2的区域到切片的中心区域的距离大于62mm也删除该连通区,改为大于1500 mm2的区域到切片的中心区域的距离大于92mm也删除该连通区。 并且在最后一步,不只保留最大的连通区,同时保留最大的两个连通区。 2.模型网络结构我们的网络如图所示,整体上是采用Unet+Resnet的思想。 里面每个Resnet Block都是由多个卷积层和bn层和relu层组成的。 我们只展示主体结构(整体深度大概150多层):3.整体优化思路3.1 数据优化肺部切割优化:这块其实没有完美的方法能把所有的肺一次性都切好。 具体的思路我们已经在第1章数据预处理部分写出来了:我们会先切一遍,然后将切肺中切的不好的,再调参数重新切一次。 10mm 以下结节的训练数据增强。 我们在没做数据增强的情况下跑出来的模型,在验证集上漏掉了不少10mm以下的结节,所以对这部分的结节做了增强。 3.2 工业界优化思路:模型架构 > 模型网络我们的优化思路非常的工业界,用更多的计算资源,和更复杂的模型架构,并不把大量的时间用在调模型网络上面。 3.3 层次化Hard Mining业界两套网络的做法比较普遍,比如用Unet切割或Faster RCNN检测,用3D CNN分类,如下图所示。 我们用的是如下统一的一套模型架构,即3D Faster RCNN的RPN网络,没有后续的全连接做分类,也并没有再在后面接一套3D CNN来做降假阳。 能减少需要调节的网络参数。 该hard mining的过程,其实就是用上一层的模型作为下一层的输入,每一层的训练数据都选取比上一层更难分的。 这套架构,无需2套网络,只需要选择一套较深的网络。 根据我们的经验,采取层次化模型训练,第二层模型froc能比第一层效果提升0.05,第三层能比第二层提升0.02。 3.4 LOSS 函数的设计在计算loss函数的时候,我们做了2点优化。 1.在使用hard mining的时候,每个batchsize里面负例的个数会明显多于正例。 为了防止算loss的时候被负例主导。 我们将loss函数分成3个部分,负例的loss,正例的loss和边框的loss。 2.在上一节提到的层次化hard mining,我们在最后一层训练模型的时候,会修改loss函数的计算,对于分错的负例和正例,做加权。 这个思路和focal loss是很像的。 比如:红框里面的部分,本来是负例,却以很大的概率被分成正例,这部分在算loss的时候权值就大些。 红框外面的部分权值就小些。 4.本次比赛的关键点总结:1) 解决了基于Intel extended Caffe的150多层深度网络的 3D Faster RCNN RPN网络收敛问题。 可以从2个方向来解决(线下Phi卡平台均已验证过)。 a)将 drop out设置为 0.1。 缺点是会容易过拟合。 b)先训练一个crop size为32的模型用这个模型做pre train model,训练crop size 64的模型依次类推。 直到完成crop size为128的模型训练由于时间关系,我们并未比较这2种思路的效果。 比赛中使用的是第1个思路,收敛的更快些。 2) 提出层次化Hard Mining的训练框架。 并没有采用常见的,unet做分割+3D CNN降假阳 或者 2d faster rcnn做检测+3D CNN降假阳的思路。 我们只用了一套网络。 减少了需要调节的网络参数。 3) 重新设计了loss函数,防止负例主导loss的计算, 并且在降低loss的过程中,更聚焦于分错的训练样本。 5. 经验总结:我们团队虽然过往深度学习架构经验多,但对医学影像处理的know how属于尚在探索之中。 所以,我们的优化思路,是用更多的计算资源,和更复杂的模型架构,来弥补没有专用模型网络积累的短板。 在第一轮比赛时通过调用比较充足的计算资源时效果比较显著,但在第二轮限定计算资源的多CPU的框架上,比较受限于计算资源及时间。 在计算资源比较充沛的情况下,选取比较深的Resnet效果会明显。 在资源受限的实际场合或者现实的生产环境,我们有两点启发:学会认同重复造轮子的基础性工作。 第一轮比赛我们是pytorch框架,第二轮按要求在caffe上实现,特别是在Intel Extended Caffe对3D支持有限,重写了不少很基础的模块,这种貌似重复造轮子的工作,对我们提出了更高的要求,但也锻炼了我们深入到框架底层的能力,从而对不同框架的性能特点有更深的认识,这种重写甚至还因此帮我们找到我们第一版pytorch代码里detect部分存在的一个bug。 根据资源灵活优化训练策略乃至模型。 我们的3D Faster RCNN 初期在Extended Caffe 上过于耗时,但因为在计算资源充足环境下我们的做法比较有效,所以没有去考虑一些更快的检测算法,比如SSD、YOLO等,这点也算是路径依赖的教训了。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。