51c深度学习~合集7

我自己的原文哦~         https://blog.51cto.com/whaosoft/12464014

#finetune后的模型参数,还可以继续怎么玩?

通过对不同下游任务的task vectors进行简单的计算,组合得到的新参数(构成一个线性子空间),可以扩展出更多的使用场景。

看到了一篇很有意思的paper,文章对预训练和finetune之后的模型参数之差(task vector = 两个weight按位相减)做了分析,发现了一些奇妙的用法:通过对不同下游任务的task vectors进行简单的计算,组合得到的新参数(构成一个线性子空间),可以扩展出更多的使用场景,如:功能抑制(遗忘),多任务学习,迁移学习。

Editing Models with Task Arithmetic:https://arxiv.org/abs/2212.04089

以下, 我们记task vector 为

我们可以构造一系列新的参数

1. 反向即遗忘

也就是

显然,这可以让模型的效果变差,有多差呢?

作者用CLIP分别在八个下游任务上finetune,然后反向,并观察在ImageNet上zero-shot效果的变化。

同时,还对比了另外两种做法,一是把梯度下降改成梯度上升,也就是loss最大化,另一种是改变 τ\tau\tau 的方向,每一层随机一个方向(长度不变)。

结果非常amazing啊,取反后在目标任务上的平均效果有了明显的下降,但在ImageNet上只相对下降了不到5%,也就是实现了部分的遗忘(精准控制)。

上面是一个负面的用例,那这种方法有什么好的使用场景呢?文中也给了一个在text generation中降低文本恶意的例子,就好比先让模型学会骂人,再跟它说,刚才学的都是不对的。

2. 相加即多任务

把多个任务的task vector求加权和(,文中只做了简单相加)之后,可以得到能用于更多场景的组合vector。当然,和直接多任务最优化的效果还是有差异的(接近8个点),但好处是方便。

这些任务本身相关性不高。

而当任务之间有一定的相关性时,可能会够起到1+1>2的效果。

可以算是以finetune为抓手,沉淀多套模型,以参数赋能打出的组合拳。

3. Task Analogy

最有意思的部分来了。众所周知,以word2vec为代表的(静态)词向量当年能够大火的原因之一是word analogy,也就是那个著名的例子:king - man + woman = queen。同理,如果两个任务有一定的共性,那么,是否可以通过构造特色特色来实现任务的迁移呢?

文中给了两个例子

1.

其中yelp和amazon是两个域,sent指的是情感分类任务,lm是在各自语料上finetune的语言模型。

2.

其中lion indoors(室内的狮子)的样本很少甚至没有,但是可以通过组合outdoor lion和indoor dog得到。

4. 分析

文中没有给出理论上的分析,这里仅做一些猜测。

文中使用的预训练模型基本都是transformer-based,可以粗略地理解为模式匹配+kv记忆。finetune强化了下游任务的模式敏感度(QK相似度提升),补充/加强了任务相关的特有记忆(FFN的近似查找敏感度提升)。这一部分增量是有一定的可加性的(激活函数会salient)。所以可以认为,task vector相当于特定任务神经元连接的加强,反之,也就是减弱,甚至抑制。而对于多任务场景,任务之间只要没有明显的冲突(夹角大于90度),相加不会有明显问题。

因为,所以,新的参数可以看成是原模型和finetune之后参数的插值。finetune时learning rate的选择对task vector的效果有一定影响。如果太大,新参数可能会跳到另一个局部最优点附近,而此时,并不能保证和pretrain model之间没有barrier(也就是不一定有mode connectivity),插值的参数可能不再是最优的。

文中只考虑了来自于同一个预训练模型的task vector之间的组合,那是否可以结合weight alignment一类的方法组合来自不同上游的vector呢?

task vector的可插拔性是它最大的优点,也就是说达到了一定程度上的模块化,可以组合出不同类型的下游任务,比如:结合1,3,如果只有英文toxic data,是否可以快速组合出低恶意的中文model呢。

同样是模型参数,为什么word embedding和entity embedding不能直接相加?因为task vector不是直接的参数,只是差分。

最后,再从实用的角度来说,虽然相比于直接finetune还是有一定差距,但是作为迁移任务的初始化参数可能是一个不错的选择。

#SAOD~

目标检测创新:一种基于区域的半监督方法,部分标签即可 ,研究表明,当训练数据缺少注释(即注释稀疏的数据)时,目标检测器的性能会显着下降。

论文地址:https://arxiv.org/pdf/2201.04620v1.pdf

研究表明,当训练数据缺少注释(即注释稀疏的数据)时,目标检测器的性能会显着下降。现在的方法侧重于以伪标签的形式或通过在训练期间重新加权未标记框的梯度来解决缺失真实标注的问题。

在这项工作中,研究者重新审视了稀疏注释目标检测的公式。研究者观察到稀疏注释的目标检测可以被认为是区域级别的半监督目标检测问题。基于这一见解,研究者们提出了一种基于区域的半监督算法,该算法可以自动识别包含未标记前景对象的区域。然后,提出的算法以不同方式处理标记和未标记的前景区域,这是半监督方法中的常见做法。为了评估所提出方法的有效性,对PASCAL-VOC和COCO数据集上稀疏注释方法常用的五个分割进行了详尽的实验,并实现了最先进的性能。除此之外,还表明新提出的方法在标准半监督设置上实现了竞争性能,证明了新方法的强度和广泛适用性。

传统的目标检测方法假设训练数据集被详尽地标记。这种检测器的性能对标记数据的质量很敏感。用于训练目标检测器的数据中的噪声可能是由于噪声类标签或不正确/丢失的边界框而产生的。在这项工作中,研究者处理了缺少类标签和边界框注释的更难的问题,即稀疏注释的存在。稀疏注释目标检测(SAOD)是在训练数据中存在缺失注释的情况下提高目标检测鲁棒性的问题。这个问题在当前至关重要,因为获取众多数据集可能既昂贵又费力。另一种方法是使用计算机辅助协议来收集注释。然而,这些方法受到嘈杂/缺失标签的影响。因此,必须调整当前的目标检测器来解决稀疏注释的问题。

问题的症结在于训练期间分配标签的方式。稀疏注释减少了真正的正样本并将假负样本引入训练,从而降低了网络性能。虽然这过于简单化了,但它可以更好地了解正在处理的问题。研究者建立在一个简单的观察之上,即SAOD是区域级别的半监督目标检测(SSOD)。然而,未标记的数据,在我们的例子中是包含前景对象的区域,是先验未知的,必须被识别。因此,SSOD方法不能直接应用于SAOD,因为当前的方法假设一个已知的未标记集以及一个详尽标记的训练集。同样,最近提出的SAOD方法在训练期间丢弃所有没有单个注释的图像,并且不能像SSOD方法那样真正利用未标记数据的力量。分别在上图的第1行和第2行中说明了SSOD和SAOD。

假设稀疏注释的目标检测方法应该是一个很好的半监督学习器,因为SSOD中的未标记图像可以被视为SAOD的缺失注释。我们在上图的第3行展示了这个公式。

所提出的方法如下图所示,由一个标准的骨干网络组成,该网络从图像的原始视图和增强视图中提取特征。

一个通用的RPN将骨干网络提取的特征连接起来,生成一组通用的候选区域。候选区域b可以属于三个组之一,即:

  • 标记区域b∈ Bl
  • 未标记区域b∈Bul
  • 背景区域b∈ Bbg

对于给定的一组真实标注,可以自动识别第一组,即标记区域。然后问题就变成了从背景区域中识别和分离第二组,即未标记区域。给定所有候选区域,pseudo-positive mining(PPM)步骤识别未标记区域并将它们与背景区域分离。受半监督方法的启发,标记和未标记区域分别受到监督和非监督损失的监督。我们在下面详细描述每个阶段。

Feature Extraction

给定图像I,计算表示为A(I)的I的增强版本。在这项工作中,我们使用随机对比度、亮度、饱和度、光照和边界框以级联方式擦除以生成A(I)。 一个检测器骨干网络用于分别从I和A(I)中提取两个特征fo和fa。

Common RPN (C-RPN)

传统的两阶段目标检测器使用区域提议网络(RPN)来生成感兴趣区域(ROI)。fo和fa这两个特征使用RPN生成两组不同的ROI。对两组ROI进行操作增加了识别标记区域、未标记区域和背景区域的难度,同时增加了处理时间。此外,对于关联,必须执行匹配算法,如基于IoU)分数的Kuhn-Munkres算法,以获得输入图像的一组通用标记、未标记和背景框。由于不完美的匹配,此过程可能会很嘈杂。为了避免这种情况,提出了一种连接fo和fa以获得ROI的C-RPN。

Pseudo Positive Mining

给定来自C-RPN的ROI,下一步是从标记区域和背景区域中识别未标记区域。基于我们的观察,即使在使用稀疏注释进行训练时,RPN也可以可靠地区分前景和背景区域,我们广泛依赖RPN的分数来识别未标记区域。

首先,根据可用的Ground Truth,所有分配为正的ROI都从C-RPN的输出中删除。接下来,所有具有大于阈值(本工作中为0.5和IoU小于阈值(本工作中为0.2)且具有任何GT的ROI都被视为未标记区域。剩余的ROI分配给负样本。我们稍后会证明这个简单的步骤可以提高RPN的召回率。请注意,由于阈值不同,一些未标记的区域可能会被错误地分配给负样本。这些地区将在后续阶段得到照顾。

Losses

在pseudo positive mining步骤之后,来自C-RPN的ROI被分为标记、未标记和背景区域。首先,ROI池化层从特征fo中提取标记区域和背景区域的区域特征,然后将其馈送到检测头。检测头预测每个区域的类别概率和边界框。稀疏GT用于监督这些预测,方法是将交叉熵损失应用于标记和背景区域的分类,以及平滑L1用于标记区域的边界框回归:

最后,对未标记区域执行与类别无关的NMS,以删除导致Nu唯一区域的重复项。Nu未标记区域与fo和fa一起通过ROI池化层和检测头,分别获得fdo和fda。应用了如下所示的一致性正则化损失,它强制原始区域和增强区域的特征彼此一致。 

实验

Sparsely annotated object detection在COCO的结果

Sparsely annotated object detection在VOC结果

 将使用可用的GT(顶部)训练的“普通”模型的输出与使用新提出的方法训练的模型(底部)进行比较的定性结果。显示类别置信度分数大于0.9的预测。红色:人,青色:狗,紫色:马,黄色:时钟,绿色:停车标志,蓝色:停车计时器,紫色:长颈鹿,橙色:盆栽,黑色:冲浪板,深绿色:船。 

#HaBa~

带来一个蒸馏相关哦~~显著提升下游模型性能

将合成数据集分解为两个部分:数据幻觉器网络和基础数据。数据幻觉器网络将基础数据作为输入,输出幻觉图像(合成图像)。该方法得到的合成数据集在跨架构任务中比基准方法取得了精度10%的提升。

论文链接: https://openreview.net/pdf?id=luGXvawYWJ

代码链接: https://github.com/Huage001/DatasetFactorization

深度学习取得了巨大成功,训练一般需要大量的数据。存储、传输和数据集预处理成为大数据集使用的阻碍。另外发布原始数据可能会有隐私版权等问题。

数据集蒸馏(Dataset Distillation)是一种解决方案,通过蒸馏一个数据集形成一个只包含少量样本的合成数据集,同时训练成本显著降低。数据集蒸馏可以用于持续学习、神经网络架构搜索等领域。

最早提出的数据集蒸馏算法核心思想即优化合成数据集,在下游任务中最小化损失函数。DSA( Dataset condensation with differentiable siamese augmentation)、GM( Dataset condensation with gradient matching)、CS(Dataset condensation with contrastive signals)等方法提出匹配真实数据集和合成数据集的梯度信息的算法。MTT(Dataset distillation by matching training trajectories)指出由于跨多个步骤的误差累计,单次迭代的训练误差可能导致较差的性能,提出在真实数据集上匹配模型的长期动态训练过程。除了匹配梯度信息的方法,DM(Dataset condensation with distribution matching)提出了匹配数据集分布,具体方法是添加最大平均差异约束( Maximum Mean Discrepancy,MMD)。

本文方法将合成数据集分解为两个部分:数据幻觉器网络(Data Hallucination Network)和基础数据(Bases)。数据幻觉器网络将基础数据作为输入,输出幻觉图像(合成图像)。在数据幻觉器网络训练过程中,本文考虑添加特殊设计的对比学习损失和一致性损失。本文方法得到的合成数据集在跨架构任务中比基准方法取得了精度10%的提升。

方法 

基与幻觉器 

先前数据集蒸馏方法中,为了在下游模型中输入和输出的形状保持一直,合成数据的形状需要与真实数据相同。由于幻觉器网络可以使用空间和通道变换,本文方法没有形状相同限制。 

对抗性对比约束

分解训练方法

与先前的数据集蒸馏方法训练范式类似,合成数据集按照迭代算法更新。每一个迭代周期,随机选取幻觉器和基,形成若干幻觉器-基组合。训练的损失函数包含知识蒸馏损失与一致性损失:

实验

与SOTA方法的比较结果。比较的方法包括核心集算法(Coreset),数据集蒸馏方法(元学习方法DD、LD,训练匹配方法DC、DSA、DSA,分布匹配方法DM、CAFE)和本文方法Factorization。超参数,每一类合成样本数(IPC)[1,10,50],本文的每一类基数量(BPC)[1,9,49]。

下图给出了实验结果。可以看出本文方法取得了最高的精度,在合成数据集样本数小于1%时性能差异最为显著。  

与不同合成数据集生成算法和不同卷积神经网络模型组合的比较实验。在AlexNet网络的实验中,本文的方法与MTT相比最高取得了17.57%的性能提升。 

不同类别是否共享幻觉器的Ablation实验。在相同的BPC条件下,较少的合成样本数情况下不共享幻觉器的方法(w/o share)可以获得更好的性能。较多的BPC情况下,不共享幻觉器方法不能获得更好的性能。主要原因:1)共享幻觉器方法可以获得数据集的全局信息。2)不共享幻觉器的方法给优化过程较大的负担 

 本文方法基和幻觉器生成图像的可视化如下:

#Single-Shot

不同特征尺度的不一致是基于特征金字塔的single-shot检测器的主要限制。这是自适应特征融合用于Single-Shot目标检测

链接: https://pan.baidu.com/s/1C_9AS3czjhpx_hffNAPRgg  密码: oow3

金字塔特征表示是解决目标检测中尺度变化挑战的常见做法。然而,不同特征尺度的不一致是基于特征金字塔的single-shot检测器的主要限制。在这项工作中,研究者提出了一种新颖的数据驱动的金字塔特征融合策略,称为自适应空间特征融合 (ASFF)。它学习了空间过滤冲突信息以抑制不一致性的方法,从而提高了特征的尺度不变性,并引入了free inference overhead。

借助ASFF策略和YOLOv3的可靠基线,在MS COCO数据集上实现了最佳速度-准确度权衡,60FPS时AP为38.1%,45FPS时AP为42.4%,29FPS时AP为 43.9%。

目标检测是各种下游视觉任务中最基本的组成部分之一。近年来,由于深度卷积神经网络(CNN)和注释良好的数据集的快速发展,目标检测器的性能得到了显着提高。然而,在大范围内处理多个对象仍然是一个具有挑战性的问题。为了实现尺度不变性,最近最先进的检测器构建了特征金字塔或多级特征塔。

尽管这些高级研究提供了更强大的特征金字塔,但它们仍然为尺度不变预测留下了空间。SNIP给出了一些证据,它采用尺度归一化方法,在多尺度图像金字塔的每个图像尺度上选择性地训练和推断适当大小的目标,具有多尺度测试的探测器,实现了基于金字塔特征的结果的进一步改进。然而,图像金字塔解决方案急剧增加了推理时间,这使得它们不适用于实际应用。

同时,与图像金字塔相比,特征金字塔的一个主要缺点是在不同尺度上的不一致,特别是对于single-shot检测器。具体来说,当检测具有特征金字塔的目标时,采用启发式引导特征选择:大实例通常与上层特征图相关联,小实例与下层特征图相关联。当一个对象在某一层的特征图中被分配并被视为正样本时,其他层的特征图中的相应区域被视为背景。因此,如果图像中同时包含小物体和大物体,则不同层次的特征之间的冲突往往会占据特征金字塔的主要部分。这种不一致会在训练期间干扰梯度计算,并降低特征金字塔的有效性。一些模型采用了几种尝试性的策略来处理这个问题。比如将相邻级别的特征图的相应区域设置为忽略区域(即零梯度),但这种缓解可能会增加相邻级别特征的较差预测。TridentNet创建了多个具有不同感受野的特定尺度分支,用于尺度感知训练和推理。它脱离了特征金字塔以避免不一致,但也错过了重用其更高分辨率的地图,从而限制了小实例的准确性。

新框架分析

新框架采用YOLOv3框架,因为它简单高效。在YOLOv3中,有两个主要组件:一个高效的骨干网(DarkNet-53)和一个三层的特征金字塔网络。

此外,许多研究表明,anchor-free pipeline通过更简单的设计有助于显着提高性能。为了更好地证明提出的ASFF方法的有效性,基于这些先进技术构建了一个比原点强得多的基线。

 Adaptively Spatial Feature Fusion

与以前使用元素总和或连接来集成多级特征的方法不同,关键思想是自适应地学习每个尺度的特征图融合的空间权重。该pipeline如上图所示,它由两个步骤组成:identically rescalingadaptively fusing

  • Feature Resizing

由于YOLOv3中三个层次的特征具有不同的分辨率和不同的通道数,我们相应地修改了每个尺度的上采样和下采样策略。对于上采样,首先应用一个1×1的卷积层将特征的通道数压缩到第l级,然后分别通过插值来提升分辨率。对于1/2比率的下采样,我们只需使用步长为2的3×3卷积层来同时修改通道数和分辨率。对于1/4的比例,我们在2-stride卷积之前添加了一个2-stride最大池化层。

  • Adaptive Fusion

令x表示从第n级调整到第l级的特征图上位置(i, j)处的特征向量。建议将相应级别l的特征融合如下:

实验对比及可视化

在COCO val-2017数据集上的比较

COCO val-2017上检测结果的可视化以及每个级别的学习权重标量图。在红色框中放大了第3级的热图,以便更好地可视化。

当一张图像具有多个不同大小的对象时,更多的定性示例。

#MRECG

解决量化误差振荡问题,让MobileNetv2的能力超过ResNet家族,这里说下PTQ中的振荡问题,为了解决振荡问题,作者提出了一种新的混合重构粒度(MRECG)方法,在具有2/4位的MobileNetV2中实现了58.49%的Top-1准确率,这大大超过了当前的SOTA方法。

训练后量化(PTQ)由于其数据隐私性和低计算成本,被广泛认为是实际中最有效的压缩方法之一。作者认为PTQ方法中存在一个被忽视的振荡问题。

在本文中主动探索并提出了一个理论证明,来解释为什么这样的问题在PTQ中是必不可少的。然后,试图通过引入一个原则性的、广义的理论框架来解决这个问题。特别是,首先公式化了PTQ中的振荡,并证明了问题是由模块容量的差异引起的。为此,作者定义了数据依赖和无数据情况下的模块容量(ModCap),其中相邻模块之间的差异用于测量振荡程度。然后通过选择Top-k差分来解决该问题,其中相应的模块被联合优化和量化。

大量实验表明,本文的方法成功地降低了性能下降,并推广到不同的神经网络和PTQ方法。例如,对于2/4位ResNet-50量化,本文的方法比以前最先进的方法高出1.9%。它在小模型量化上变得更重要,例如在MobileNetV2×0.5上比BRECQ方法高出6.61%。

深度神经网络(DNN)近年来迅速成为研究热点,在实践中被应用于各种场景。然而,随着DNN的发展,更好的模型性能通常与更深更宽网络的巨大资源消耗有关。与此同时,旨在在资源受限的场景中部署模型的神经网络压缩和加速研究领域正逐渐受到越来越多的关注,包括但不限于神经架构搜索、网络剪枝和量化。在这些方法中,量化提出了将浮点网络激活和权重转换为低bit整型,这能够加速推理或训练的速度,而性能几乎没有下降。

通常,网络量化方法分为量化感知训练(QAT)和训练后量化(PTQ)。前者通过量化微调来减少量化误差。尽管取得了显著的成果,但海量的数据需求和高昂的计算成本阻碍了DNN的普遍部署,尤其是在资源受限的设备上。因此,提出了PTQ来解决上述问题,该问题只需要少量或零校准数据来进行模型重建。由于没有量化训练的迭代过程,PTQ算法非常高效,通常在几分钟内获得和部署量化模型。然而,这种效率往往是以牺牲准确性为代价的。

PTQ通常在没有量化训练的情况下比全精度模型表现更差,尤其是在低位紧凑模型量化中。最近的一些算法试图解决这个问题。例如,Nagel等人通过量化前后损失函数的二阶泰勒展开构建新的优化函数,该函数引入了具有可学习参数的软量化,以实现自适应权重舍入。Li等人逐层改变逐块重建,并使用对角Fisher矩阵来近似Hessian矩阵,以保留更多信息。Wei等人发现,随机禁用激活量化的一些元素可以平滑量化权重的损失。

然而,作者观察到,在重建过程中,随着层或块的加深,上述所有方法都表现出不同程度的振荡,如图1的左侧子图1所示。作者认为,这个问题是至关重要的,并且在以前的PTQ方法中被忽视了。

在本文中,通过严格的数学定义和证明,回答了关于振荡问题的3个问题,如下所示:

  1. 为什么PTQ会出现振荡?为了回答这个问题,作者首先定义了模拓扑同构,它在一定程度上放宽了模等价的限制。然后,给出了在模块拓扑同构条件下模块容量的定义。在这种情况下,作者可以证明,当后期模块的容量足够大时,重建损失将突破量化误差累积的影响并降低。相反,如果后面模块的容量小于前面模块的容量,则由于放大的量化误差累积效应,重建损失急剧增加。总体而言,作者证明了PTQ重建过程中损失的振荡是由模块容量的差异引起的;
  2. 振荡将如何影响最终性能?通过随机采样大量混合重建粒度方案,观察到最终的重建误差与之前所有模块中最大的重建误差高度相关。换句话说,当振荡发生时,先前的模块显然具有较大的重建误差,从而导致PTQ的精度较差;
  3. 如何解决PTQ中的振荡问题?由于振荡是由前后模块的不同容量引起的,作者提出了混合重构粒度(MRECG)方法,该方法可以联合优化发生振荡的模块。此外,本文的方法适用于无数据和依赖数据的场景,也兼容不同的PTQ方法。

总的来说,贡献如下:

  1. 首次揭示了PTQ中的振荡问题,这在以前的算法中被忽略了。然而,作者发现,在PTQ的优化中,消除这种振荡是至关重要的。
  2. 从理论上表明,这种振荡是由相邻模块的能力差异引起的。小的模块能力会加剧量化误差的累积效应,使损失迅速增加,而大的模块能力则会减少累积量化误差,使损失减少。
  3. 为了解决振荡问题,作者提出了一种新的混合重构粒度(MRECG)方法,该方法利用损失度量和模块容量来优化数据依赖和无数据场景下的混合重构细粒度。前者以适度较高的开销找到全局最优,因此具有最佳性能。后者更有效,性能略有下降。
  4. 在ImageNet中的各种压缩任务上验证了所提出的方法的有效性。特别是,在具有2/4位的MobileNetV2中实现了58.49%的Top-1准确率,这大大超过了当前的SOTA方法。此外,还证实了本文的算法确实消除了重建损失在不同模型上的振荡,使重建过程更加稳定。

量化

量化可以分为两类:量化感知训练(QAT)和训练后量化(PTQ)。QAT使用整个训练数据集进行量化训练,并通过网络的反向传播来更新梯度以消除量化误差。尽管QAT集成了各种训练方法以实现更高的准确性,但在某些数据隐私场景中,这一过程往往是资源密集型的,并且受到限制。本文的研究兴趣不在这个领域。

近年来,由于高效的模型部署和低数据依赖性的优势,PTQ越来越受到关注。由于不包括量化训练,PTQ算法通常使用小的校准数据集进行重建,通过优化任务损失的二阶泰勒展开项的近似值来获得更好的量化模型。

模块容量

一些常见参数会影响模块容量,例如滤波器的大小、权重参数的位宽和卷积组的数量。此外,一些研究表明,步长和剩余捷径也会影响模块容量。Kong等人表明,步长为2的卷积可以等效地被步长为1的卷积所取代。同时,替换卷积的滤波器大小大于原始卷积的滤波器尺寸,这意味着模块容量的增加。

提出的MobileNetV2包含深度卷积,它不包含通道之间的信息交换,因此它在某种程度上损害了模型性能。Liu等人认为,全精度残差结构的输入增加了量化模块的表示能力。

方法

在本节中,首先通过一个定理和推论证明了PTQ的振荡问题与模块容量高度相关。其次,构造了容量差异优化问题,并分别在依赖数据和无数据的情况下给出了两种解决方案。最后,分析了扩大校准数据的批量大小以减少期望近似误差,这表明边际效用有递减的趋势。

PTQ振荡问题

注意,忽略了AdaRound中的正则化项,这有助于收敛,因为它在每个模块优化后收敛到0。此外,为了简单起见,省略了BRECQ中的平方梯度尺度。如果2个模块具有相同数量的卷积层,并且所有相关联的卷积层具有相同的超参数,则它们被称为等效的。

量化中存在量化误差的累积效应,表现为网络中量化误差受前一层量化影响的增加趋势。由于PTQ不包含量化训练,这种累积效应在重建损失中更为明显。本文提出以下定理来证明PTQ中量化误差的累积效应会导致增量损失。

定理1

给定预先训练的模型和输入数据。如果2个相邻的模块是等效的,

补充材料中提供了详细的证明。定理1说明,在2个相邻模块相等的情况下,量化误差的累积会导致损耗的增加。

然而,由于一个或多个卷积超参数(额外的残差输入、kernel大小、通道、组、步长等)的差异,上述定理中的相邻模等价条件在实际场景中很难得到满足。

非等效模块将不可避免地导致模块容量的差异。众所周知,模块的参数数量和位宽会影响模块容量,其中这些超参数对模块容量的影响很容易量化。此外,在第2节中介绍了残差输入、卷积类型和卷积超参数都会影响模块容量,这些影响很难量化。因此,将模块拓扑同构的概念定义如下,这在一定程度上放宽了对模块等价的限制,同时使比较不同模块之间的容量成为可能。

定义1

(模块拓扑齐性)假设两个模块具有相同数量的卷积层。如果2个模块的相应卷积层的超参数除了kernel-size和通道之外是相同的,那么这两个模块在拓扑上是同构的。

然后,导出定理1的一个推论来解释为什么PTQ中会出现振荡。

推论1

假设两个相邻的模在拓扑上是同构的。如果后面模块的模块容量足够大,则损失将减小。相反,如果后一模块容量小于前一模块容量,则量化误差的累积效应会加剧。

补充材料中提供了详细的证明。根据推论1可以得出结论,PTQ在各种模型上的振荡问题是由相邻模块的容量差异过大引起的。

论文的逻辑/相关链:振荡性∝最大误差∝最终误差∝准确度

  • 振荡∝最大误差:从图6和补充材料中的类似图来看,不同算法对应的损失分布振荡越严重,损失峰值就越大。也就是说,振荡的程度与最大误差呈正相关;
  • 最大误差∝最终误差:此外,在图3中的观察结果表明,对于不同的模型,在不同的算法上,最大误差与最终误差呈正相关;
  • 最终误差∝精度:理论上,通过根据BRECQ和Adaround对精度损失函数进行泰勒展开,可以得出与性能高度相关的最终重建误差;

从经验上讲,上述论文中进行的大量实验也证明了这一说法。总之,误差的振荡程度与精度呈正相关。论文中的图1也表明,降低振荡程度有利于精度。在下一节中将介绍如何通过优化模块容量差异来消除PTQ中的振荡。

混合重建粒度

第3.1节中分析的振荡问题表明,由于模块容量的差异导致的信息丢失最终会影响PTQ的性能。由于PTQ中没有量化训练过程,即使增加后续模块的模型容量,也无法恢复这种信息损失。因此,通过联合优化具有大容量差异的模块来平滑损失振荡,从而降低最终重建损失。

从定理1和推论1可以清楚地看出,后面模块的小容量将加剧量化误差的累积效应,从而急剧增加重建损失。相反,后面模块的大容量将减少损失,并增加后面模块中信息丢失的概率。因此,本文希望两个相邻模块的容量尽可能接近。基于容量度量(CM)为包含L个模块的模型构造容量差优化问题,如下所示,

具体如图2所示。根据ModCap度量获得的混合重建粒度方案在计算上是有效的,因为它不涉及重建。然而,一旦组合了一对相邻的模块,这个组合的模块就无法比较ModCap进行进一步的组合,因为它与相邻的模块在拓扑上不同构。因此,该优化方案只能获得混合重建粒度的局部最优解。

另一方面,根据推论1,ModCap差异与重建损失的差异呈正相关,可以将重建损失本身视为容量度量。该方案可以得到全局最优解,但需要进行PTQ重建才能获得重建损失,效率相对较低。

校准数据的Batch size

PTQ需要数据集的一小部分来进行量化参数的校准和模型重建。作者注意到,方程1包含平方Frobenius范数的期望。期望值表示随机变量的平均值,取采样批次的平均值来近似重建过程中的期望值。大数定律证明了当样本大小N趋于无穷大时。

实验表明,扩大校准数据的批量可以提高PTQ的准确性。这一趋势显示出边际效用的递减。具体而言,随着批量大小的增加,PTQ精度的提高速度减慢。详见第4.3节。

实验

ImageNet Classification

在各种模型上验证了本文的算法,包括不同规模的ResNet-18、ResNet-50和MobileNetV2。

如表1所示,本文算法在与不同比特配置相对应的广泛模型上以很大的优势优于其他方法。具体而言,在ResNet-18和2/4位的MobileNetV2上实现了66.18%和57.85%的Top-1准确率,分别比SOTA高1.52%和4.93%。

其次,本文的方法在低位显示出强大的优越性。例如,在MobileNetV2×0.75上,2/4位PTQ量化的精度增益显著高于4/4位(5.36%对1.61%)。振荡问题可能更为严重,因为低位会导致PTQ重建误差的大小增加。因此,在低位量化时平滑这种振荡对于PTQ优化过程至关重要。

此外,本文的方法在不同规模上对MobileNetV2进行了相当大的改进,这意味着模型大小不会限制算法的性能。作者还注意到本文的算法对MobileNetV2更有效。通过观察,MobileNetV2中的振荡问题比ResNet网络家族的振荡问题更严重。深度可分离卷积增加了模块容量的差异,从而使损失振荡的幅度更大。因此,本文的算法通过解决MobileNetV2中的振荡问题,实现了相当大的性能改进。

消融实验

1、帕累托最优

在不同规模的MobileNetV2上随机采样了大量混合重建粒度方案,并使用BRECQ算法使用4/4位PTQ对这些方案进行优化,以获得采样精度。在图4中绘制了这些方案的精度分布。

此外,还分别在MobileNetV2上标记了MRECG和BRECQ算法在不同尺度下的准确性。从图4中可以观察到MRECG在所有尺度上都比MobileNetV2有稳定的准确性提高。总体而言,MRECG在模型大小的限制下实现了Pareto最优状态。

2、Component Contribution

在这里分别研究了混合重建粒度和扩展批处理大小对准确性的贡献。将通过BRECQ量化为4/4位的0.5缩放的MobileNetV2作为基线。如表2所示,混合重建粒度和扩展批量大小的准确度分别提高了1.08%和0.74%。同时,这两种方法的结合进一步提高了模型的性能。

3、缩小扩大批量规模的边际效用

根据大数定律,当样本数量增加时,样本的平均值收敛于方程1中的期望值,从而产生较小的近似误差。然而,当样本大小足够大时,由于近似误差的减小而获得的精度增益可以忽略不计。换句话说,Batch size的扩大呈现出边际效用递减的趋势。

在图5中,对一些Batch size进行了随机采样,并通过BRECQ在0.5标度的MobileNetV2上获得了4/4比特的PTQ精度。证明了采样精度的中位数,以防止异常值的影响。作者注意到,当Batch size较小时,采样精度波动更大,这意味着较小Batch size的近似误差会产生较大的噪声。随着Batch size的扩大,这种情况有所缓和。此外,可以观察到中值精度的增加,然后趋于稳定,这意味着扩大Batch size可以为PTQ带来精度增益。同时,这种收益受到边际效用递减的制约。

4、损失分布

如图6所示,给出了不同算法的损失分布。从Adaround的分布可以看出,它具有最大的振荡幅度。因此,在模型的更深层次,Adaround的重建损失迅速增加。此外,BRECQ使用块重建进行PTQ优化,这在某种程度上缓解了损耗振荡问题,从而提高了性能。

混合重建粒度通过联合优化容量差异较大的相邻模块来平滑损耗振荡。可以看出,与Adaround和BRECQ相比,MRECG的损失变化更稳定。此外,当比较不同场景下的MRECG损失时,发现基于无数据场景获得的MRECG仍然具有较小的振幅损失振荡。作者认为,这种适度的振荡与ModCap MRECG只能联合优化两个相邻模块的事实有关,这只能实现局部最优。相比之下,MRECG具有最平滑的损耗曲线。然而,这种全局最优是以延长获得量化模型的时间为代价的。

#SPARSE DETR

DETR家族又一成员,具有可学习稀疏性的高效端到端目标检测

DETR是第一个使用transformer编码器-解码器架构的端到端对象检测器,在高分辨率特征图上展示了具有竞争力的性能但计算效率低。随后的工作Deformable DETR通过将密集注意力替换为可变形注意力来提高DETR的效率,从而实现了10倍的收敛速度和性能提升。

DETRDeformable DETR使用多尺度特征来改善性能,然而,与DETR相比,encoder token的数量增加了20倍,并且编码器注意力的计算成本仍然是瓶颈。在我们的初步实验中,我们观察到即使只更新了一部分encoder token,检测性能也几乎不会恶化。受此观察的启发,研究者提出了稀疏DETR,它选择性地仅更新预期被解码器引用的标记,从而帮助模型有效地检测目标。

此外,研究者展示了在编码器中对所选标记应用辅助检测损失可以提高性能,同时最大限度地减少计算开销。我们验证了即使在COCO数据集上只有10%的encoder token,Sparse DETR也比可变形DETR实现了更好的性能。尽管只有encoder token被稀疏化,但与可变形DETR相比,总计算成本降低了38%,每秒帧数 (FPS) 增加了42%。 

近年来,我们见证了深度学习中目标检测的巨大进步和成功。已经提出了多种目标检测方法,但现有算法将与GT进行正匹配作为一种启发式方法,需要对近似重复预测进行非极大值抑制 (NMS) 后处理。最近Carion等人通过基于集合的目标消除了对NMS后处理的需要,引入了完全端到端的检测器DETR。训练目标采用匈牙利算法设计,既考虑分类成本,又考虑回归成本,并获得极具竞争力的性能。但是,DETR无法使用多尺度特征,例如特征金字塔网络,这些特征常用于目标检测,以提高对小目标的检测。主要原因是通过添加Transformer 架构增加了内存使用和计算。因此,它对小物体的检测能力比较差。

为了解决这个问题,有人提出了一种受可变形卷积 (deformable convolution) 启发的可变形注意力,并通过注意力模块中的关键稀疏化将二次复杂度降低为线性复杂度。通过使用可变形注意力,可变形DETR解决了DETR收敛速度慢和复杂度高的问题,使编码器能够使用多尺度特征作为输入,显着提高了检测小物体的性能。然而,使用多尺度特征作为编码器输入会使要处理的token量增加约20倍。最终,尽管对相同的token长度进行了有效的计算,但整体复杂性再次增加,使得模型推理甚至比普通的DETR更慢。

(a) DETR中的密集注意力需要二次复杂度。(b) Deformable DETR使用密钥稀疏化,因此具有线性复杂度。(c) Sparse DETR进一步使用查询稀疏化。Sparse DETR中的Attention也采用线性复杂度,但比Deformable DETR轻得多。 

上图说明了如何通过预测二值化解码器交叉注意力图(DAM)来学习评分网络,其中橙色虚线箭头表示反向传播路径。左边部分展示了编码器中的前向/反向传播,右边部分展示了如何构建DAM来学习评分网络。 

稀疏DETR引入了三个附加组件:(a)评分网络,(b)编码器中的辅助头,以及(c)为解码器选择前k个token的辅助头。稀疏DETR使用评分网络测量编码器token的显着性,并选择top-ρ%的token,在上图中称为(1)。在仅精炼编码器块中选定的token后,辅助头从编码器输出中选择前k个token,用作解码器对象查询。这个过程在上图中被称为(2)。此外,我们注意到每个编码器块中的附加辅助磁头在提高性能方面发挥着关键作用。仅将稀疏编码器token传递给编码器辅助头以提高效率。编码器和解码器中的所有辅助头都经过Hungarian损失训练,如Deformable DETR中所述。 

实验及可视化

#最大似然估计和最大后验估

深入浅出最大似然估计和最大后验估。

TLDR (or the take away)

  • 频率学派 - Frequentist - Maximum Likelihood Estimation (MLE,最大似然估计)
  • 贝叶斯学派 - Bayesian - Maximum A Posteriori (MAP,最大后验估计)

概述

有时候和别人聊天,对方会说自己有很多机器学习经验,深入一聊发现,对方竟然对MLE和MAP一知半解,至少在我看来,这位同学的机器学习基础并不扎实。难道在这个深度学习盛行的年代,不少同学都只注重调参数?

现代机器学习的终极问题都会转化为解目标函数的优化问题,MLE和MAP是生成这个函数的很基本的思想,因此我们对二者的认知是非常重要的。这次就和大家认真聊一聊MLE和MAP这两种estimator。

两大学派的争论

抽象一点来讲,频率学派和贝叶斯学派对世界的认知有本质不同:频率学派认为世界是确定的,有一个本体,这个本体的真值是不变的,我们的目标就是要找到这个真值或真值所在的范围;而贝叶斯学派认为世界是不确定的,人们对世界先有一个预判,而后通过观测数据对这个预判做调整,我们的目标是要找到最优的描述这个世界的概率分布。

这里有两点值得注意的地方:

  • 随着数据量的增加,参数分布会越来越向数据靠拢,先验的影响力会越来越小
  • 如果先验是uniform distribution,则贝叶斯方法等价于频率方法。因为直观上来讲,先验是uniform distribution本质上表示对事物没有任何预判

MLE - 最大似然估计

Maximum Likelihood Estimation, MLE是频率学派常用的估计方法!

最后这一行所优化的函数被称为Negative Log Likelihood (NLL),这个概念和上面的推导是非常重要的!

我们经常在不经意间使用MLE,例如

  • 上文中关于频率学派求硬币概率的例子,其方法其实本质是由优化NLL得出。本文末尾附录中给出了具体的原因 :-)
  • 给定一些数据,求对应的高斯分布时,我们经常会算这些数据点的均值和方差然后带入到高斯分布的公式,其理论依据是优化NLL
  • 深度学习做分类任务时所用的cross entropy loss,其本质也是MLE

MAP - 最大后验估计

Maximum A Posteriori, MAP是贝叶斯学派常用的估计方法!

再稍微补充几点:

  • 我们不少同学大学里学习概率论时,最主要的还是频率学派的思想,其实贝叶斯学派思想也非常流行,而且实战性很强
  • CMU的很多老师都喜欢用贝叶斯思想解决问题;我本科时的导师朱军老师也在做贝叶斯深度学习(https://arxiv.org/abs/1709.05870)的工作,有兴趣可以关注一下。

后记

有的同学说:“了解这些没用,现在大家都不用了。”这种想法是不对的,因为这是大家常年在用的知识,是推导优化函数的核心,而优化函数又是机器学习 (包含深度学习) 的核心之一。这位同学有这样的看法,说明对机器学习的本质并没有足够的认识,而让我吃惊的是,竟然有不少其他同学为这种看法点赞。内心感到有点儿悲凉,也引发了我写这篇文章的动力,希望能帮到一些朋友 :-)

#代码实现深度学习研究论文の要点

深度学习领域涌现了大量研究论文,使得对现有研究的复现和使用成为一项挑战。原因在于缺乏作者提供的开源实现。本文作者就这一问题详细介绍了将深度学习研究论文实现为代码的步骤及技巧,并辅以举例讲解。

导读

如果深度学习是一种超能力,那么将理论从论文转化为可用的代码就是一种超超能力。

为什么要去复现机器学习研究论文?

正如我所说的,能够将一篇论文转换成代码绝对是一种超超能力,尤其是在像机器学习这样每天都在快速发展的领域。大多数研究论文来自大型科技公司或大学里的人,他们可能是博士,也可能是研究前沿技术的人。还有什么比复现这些顶尖专业人士的研究成果更酷的呢?另一件需要注意的事情是,行业中对这些能够将研究论文进行代码复现的人需求量很大。一旦你掌握了撰写研究论文的窍门,你就会处于与这些研究人员同等的地位。这些研究人员也是通过阅读和复现研究论文的练习获得了这些技能。

如何来阅读和对论文进行复现的?

你可能会说,“嗯,我对深度学习算法有一个大致的了解,像全连接网络,卷积神经网络,循环神经网络,但问题是,我想开发SOTA(最新的)语音克隆AI,但我对语音克隆一无所知:(”。

好吧,这里是你的答案,我的方法的一些部分取自“Andrew Ng关于阅读论文的建议”:https://www.youtube.com/watch?v=733m6qBH-jI&list=PLoROMvodv4rOABXSygHTsbvUz4G_YQhOb&index=8。

如果你想了解一个特定的主题:

  1. 收集与特定主题相关的5-6篇论文(你可以通过arxiv或类似的网站找到与某个主题相关的论文)。
  2. 不要把每一篇论文完整读完,而是浏览所有的论文,然后挑一篇你感兴趣的,或者如果你心里有一篇特别的论文,那就挑一这篇。
  3. 仔细阅读摘要,从高层次上理解其中的思想,看看你的兴趣是否还在继续,如果还在,那就继续浏览图片,看看你是否能对论文内容做出假设。
  4. 现在,逐行仔细阅读引言,因为论文所包含的大部分内容将在这里用最简单的方式和最小的数学来解释。
  5. 如果你愿意,你可以跳过第一轮的数学方公式,但是不要跳过那些有熟悉的希腊字母的数学公式。
  6. 在任何情况下,如果你陷入了困境或某些词让你迷惑不解,不要犹豫,去google 吧,没有人生来什么都懂。
  7. 在完成第一关之后,你将处于一种对论文试图证明或改进的地方有一个高层次的理解的状态。
  8. 在第二步中,尝试理解本文中的几乎所有内容,如果遇到任何伪代码,尝试将其转换为你选择的python库(PyTorch、TensorFlow……)
  9. 你可以阅读更多的论文,并通过阅读每篇论文的参考资料部分来更好地了解该领域。

💡这里是一些高效理解论文的小技巧:

  • 如果你是研究论文的初学者,最好在阅读论文之前阅读一些与该主题/研究论文相关的博客文章和视频。这使你的工作更容易。
  • 在复现论文时,一定要做笔记,并把重要的地方圈出来,以方便做参考。
  • 如果你是研究论文实现的新手,并且在任何地方遇到了困难,那么尝试一下开源实现并看看其他人是如何做到这一点的,这不是一个坏主意。

注意: 不要把第三点当成常规操作,因为这会导致你的学习曲线会下降,你会过拟合。你应该发展自己的阅读和实现论文的方法,这只有通过开始才有可能,所以上面的步骤将帮助你开始。Andrew Ng表示,如果你在一个主题上能读5 - 10篇论文,比如语音克隆,你会在实现的时候有一个较好的状态, 如果你读了50 - 100篇论文,那在这个主题上,你可以进行研究或做前沿技术的研发。

来讨论一篇论文

  1. 高层次的概要

现在,你已经了解了如何阅读论文,让我们阅读并为自己实现一篇论文。我们将研究Ian Goodfellow的论文 —— 生成对抗网络(GAN),并使用PyTorch实现同样的功能。

论文摘要中对论文内容进行了详细的概述。摘要告诉我们,研究人员提出了一个包含两种神经网络的新框架,它们被称为“生成器”和“判别器”。不要被名字弄懵,它们只是两个神经网络的名字。

但在摘要部分要注意的主要一点的是,上面提到的生成器和判别器会相互竞争。好了,让我来讲清楚一点。

让我们以用GANs生成不存在的新人脸为例。

该发生器生成与真实图像尺寸(H×W×C)相同的人脸,并将其喂给判别器,判别器判断该图像是由发生器生成的伪图像还是真实的人脸。

现在你可能会有一个问题,“嗯,这个判别器是如何辨别真假图像的?”,下面就是你的答案:

判别图像是否是真实的是一个分类问题,也就是说,判别器必须分辨图像是真实的还是假的(0或1)。所以我们可以像训练狗和猫分类卷积神经网络那样来训练判别器,而本文中用的是全连接的网络。

💡DCGAN是另一种类型的GAN,使用卷积神经网络代替全连接网络会有更好的结果。所以我们训练判别器的方式是将图像输入判别器中,输出0或1,也就是说,假的或真的。

当我们训练我们的判别器时,我们将把由生成器生成的图像传递给判别器,并分类它是真的还是假的。生成器调整它所有的权值,直到它能够欺骗分类器预测生成的图像是真实的。

我们将给生成器一个随机概率分布(一个随机张量),生成器的职责是改变这个概率分布,以匹配真实图像的概率分布。

这些是我们在执行代码时应该遵循的步骤:

→加载包含真实图像的数据集。

→创建一个二维随机张量(假数据的概率分布)。

→创建判别器和生成器模型。

→在真实图像和虚假图像上训练判别器。

→将假图像的概率分布送到生成器中,并用判别器测试是否可以区分出这个图像是由生成器生成的虚假图像。

→调整生成器的权值(通过随机梯度下降)直到判别器无法区分真假图像。

你可能有几个疑问,但现在没关系,一旦我们实现了理论代码,你会了解它是如何工作的。

  1. 损失函数

在我们实现代码之前,我们需要一个损失函数,以便我们可以优化我们的生成器网络和判别器网络。

该判别器模型是个二分类问题,因此我们使用二元交叉熵损失作为判别器,也可以使用本文讨论的自定义损失函数。

本文的损失函数:[log D(x)] + [log(1−D(G(z)))]

x →真实图像

z →假数据或噪声(随机张量)

D →判别模型

G →生成模型

G(z) →将假数据或噪声送到生成器中(输出是假图像)

D(x) →将真实图像送到判别器中(输出是0或1)

D(G(z)) →将假数据送到生成器中,将生成器输出的图像送到判别器中得到预测(输出是0或1)

如果你想用论文中的损失函数,让我来解释一下:

本文认为,对于判别器,我们需要将上述损失函数最大化。

让我们看方程的第一部分:

— D(x) 输出0或1,所以,当我们最大化 *log[D(x)]*时,这使得当把真实图像喂给判别器的时候,输出趋向于1,这正是我们所需要的。

我们再看方程的第二部分:

— G(z) 输出一张图像,和真实的图像具有相同的尺寸,现在将假图像送到判别器*D(G(z))*中,当最大化判别器的输出的时候,判别器的输出会趋向于1,此时,当我们最大化[1 − D(G(z))]的时候,*D(G(z))*会得到趋向于0的值,这也正是当我们将假图像送到判别器的时候所需要的。

注意: 你可以在方程上加上一个负号,然后将损失函数转化为判别器的最小化问题这比最大化更容易。

对于生成器,我们需要最小化上述方程,但本文只考虑方程的第二部分*[log(1−D(G(z)))]*进行最小化。

— 当我们最小化*D(G(z))*时,判别器输出一个接近于0的值,并且方程的总输出接近于1,这就是我们的生成器想要达到的,当从生成器得到假图像时,欺骗鉴别器预测1(真实)。

开始写代码!

我已经在谷歌colab中完成了代码实现,你试试在谷歌colab或jupyter中写代码。1、导入所需的库—

import torch
from torch import nn
from torch import optim
from torchvision.transforms import transforms
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

from tqdm.notebook import tqdm
import warnings
warnings.filterwarnings("ignore")

2、我们将使用一个单一的图像作为真实的图像,以更快的训练和得到结果,所以图像生成的生成器将类似于这个图像,你也可以使用一组图像的数据,这一切由你。我们将使用_PIL library_将图像加载为PIL image,然后使用_torchvision transforms_调整大小并将图像转换为张量,然后创建大小为(1×100)的伪噪声来生成图像。   

transform = transforms.Compose([
                                transforms.Resize((32, 32)),
                                transforms.ToTensor()
])

flat_img = 3072  #32×32×3 --size of flattened image

img = Image.open('truck.jpeg')
real_img = transform(img)

torch.manual_seed(2)
fake_img = torch.rand(1, 100)

plt.imshow(np.transpose(real_img.numpy(), (1, 2, 0)))
print(real_img.size())

3、创建一个判别器模型,它是一个全连接的神经网络,接收真实图像或伪图像,输出0或1。

class Discriminator(nn.Module):
  def __init__(self):
    super().__init__()
    self.linear = nn.Sequential(
        nn.Linear(flat_img, 10000),
        nn.ReLU(),
        nn.Linear(10000, 1),
        nn.Sigmoid()
    )

  def forward(self, img):
    img = img.view(1, -1)
    out = self.linear(img)

    return out

4、创建一个生成器模型,它也是一个全连接的网络,接受随机噪声并输出一个与真实图像大小相同的图像张量。

class Generator(nn.Module):
  def __init__(self):
    super().__init__()
    self.linear = nn.Sequential(
        nn.Linear(100, 10000),
        nn.LeakyReLU(),
        nn.Linear(10000, 4000),
        nn.LeakyReLU(),
        nn.Linear(4000, flat_img)
    )

  def forward(self, latent_space):
    latent_space = latent_space.view(1, -1)
    out = self.linear(latent_space)

    return out

5、初始化模型,优化器和损失函数,然后将它们移动到所需的设备(cuda或cpu)。我们在判别器中使用二元交叉熵损失,并对生成器使用本文中讨论的损失函数_log(1 - D(G(z))_。

device = 'cuda:0' if torch.cuda.is_available() else 'cpu'

discr = Discriminator().to(device)
gen = Generator().to(device)

opt_d = optim.SGD(discr.parameters(), lr=0.001, momentum=0.9)
opt_g = optim.SGD(gen.parameters(), lr=0.001, momentum=0.9)

criterion = nn.BCELoss()

6、现在我们对模型进行训练,整个GAN被训练500个大的epoch,判别器先

训练4个epoch,然后生成器再训练3个epoch。

epochs = 500
discr_e = 4 
gen_e = 3

#whole model training starts here
for epoch in tqdm(range(epochs), total=epochs):

  #discriminator training
  for k in range(discr_e):
    opt_d.zero_grad()

    out_d1 = discr(real_img.to(device))
    #loss for real image
    loss_d1 = criterion(out_d1, torch.ones((1, 1)).to(device))
    loss_d1.backward()

    out_d2 = gen(fake_img.to(device)).detach()
    #loss for fake image
    loss_d2 = criterion(discr(out_d2.to(device)), torch.zeros((1, 1)).to(device))
    loss_d2.backward()

    opt_d.step()

  #generator training
  for i in range(gen_e):
    opt_g.zero_grad()
    
    out_g = gen(fake_img.to(device))
    
    #Binary cross entropy loss
    #loss_g =  criterion(discr(out_g.to(device)), torch.ones(1, 1).to(device))

    #----Loss function in the GAN paper
    #[log(1 - D(G(z)))]
    loss_g = torch.log(1.0 - (discr(out_g.to(device)))) 
    loss_g.backward()

    opt_g.step()

7、将生成的图像与真实图像进行比较。你可以调整学习率,动量,epochs以及生成器和判别器中的层以得到更好的结果。

最后的思考

生成的图像可能分辨率不是很高,因为本文只是整个生成模型的开始。如果你仍然在坚持,你可以继续阅读DCGANs或其他论文:https://github.com/nightrome/really-awesome-gan,并实现看看那些了不起的结果,但请记住本文是这些论文的基础。

#对抗对比学习の论文

2篇对抗对比学习论文导读,一个增强 ACL 的有效性的方法和一个提升ACL的效率的技术 

基础模型 (foundation models)[1]是通过在大规模未标记数据集上进行自监督预训练 (self-supervised learning)得到的,可以通过微调 (fine-tuning)迁移到广泛的下游任务。 现有的工作表明[2],存在对抗攻击 (adversarial attacks)可以“愚弄”通过微调基础模型获得的任何下游模型。 这种对抗攻击的存在驱动我们去开发鲁棒的基础模型 (robust foundation models),该模型可以在安全关键的下游任务中具有很好的泛化性 (generalization ability)和对抗鲁棒性 (adversarial robustness)。 目前,对抗对比学习[3](adversarial contrastive learning, ACL)是构建鲁棒的基础模型的最有效方法之一。 在本篇博客中,我介绍两篇NeurIPS 2023 论文,分别提出了一个增强 ACL 的有效性的方法[4]和一个提升ACL的效率的技术[5]:

  • [NeurIPS'23] Enhancing Adversarial Contrastive Learning via Adversarial Invariant Regularization, Xu et al. 2023.

https//openreview.net/forum%3Fid%3DzuXyQsXVLF

这篇论文建立了因果理论框架来解释ACL,并根据因果框架提出了对抗不变性正则化 (adversarial invariant regularization, AIR) 来提升ACL达到了SoTA performance。

AIR在Robust Self-Supervised Learning (RobustSSL) Benchmark 上排名第一。RobutSSL Benchmark的Model Zoo(https//github.com/GodXuxilie/RobustSSL_Benchmark)提供大部分ACL方法预训练后得到的权重文件,可供大家下载使用。

https//robustssl.github.io/

https//github.com/GodXuxilie/RobustSSL_Benchmark

  • [NeurIPS'23 Spotlight] Efficient Adversarial Contrastive Learning via Robustness-Aware Coreset Selection, Xu et al. 2023.

https//openreview.net/forum%3Fid%3DfpzA8uRA95

这篇论文介绍了一种鲁棒性感知核心集选择 (robustness-aware coreset selection, RCS) 方法来加速 ACL。 RCS 不需要标签信息并搜索有助于学习到鲁棒特征的训练子集。 RCS首次将ACL应用于大规模ImageNet-1K数据集。

鲁棒基础模型 (Robust Foundation Models)

基础模型比如(GPT[6], Vision transformer[7], BLIP[8], CLAP[9])最近非常流行。 构建和利用基础模型 (foundation models) 是分为2个阶段:

  1. 预训练阶段 (pre-training):将一个模型在大规模没有标签的数据集上进行自监督预训练 (self-supervised learning)后得到预训练的基础模型;
  2. 微调阶段 (fine-tuning):在下游任务上进行微调后,基础模型可以来很好地解决下游任务,比如图像分类问题和情感分析。

但是对抗攻击可以通过在下游任务的原始输入中添加难以察觉的对抗性扰动来欺骗基础模型学习到的特征,从而输出错误的预测。

基础模型的特征很容易受到对抗性攻击,这种攻击可以让模型错误地将汽车预测为“不是汽车”。

对抗攻击的存在使得基础模型在被应用在一些安全性关键下游任务,比如医疗分析[10]和自动识别路标[11],存在潜在安全风险。因此我们需要构建鲁棒的基础模型来使得这些它们能够被可靠地应用于下游任务。

鲁棒的基础模型具有以下2个特征:

  • 依然可以很好地泛化到下游任务中;
  • 能在下游任务中,对对抗攻击具有很强的鲁棒性。

其中对抗视角的公式如下:

PGD生成对抗数据过程。PGD根据损失梯度方向一步一步将自然数据(绿色的点 在low-loss区域)更新到对抗数据(红色的点在high-loss区域)。

在每个 epoch,ACL 交替执行步骤 (1) 和 (2):

  • 步骤(1): 通过PGD生成对抗数据;
  • 步骤(2): 通过最小化对抗对比损失来更新模型参数。

如何实现ACL预训练?

你可以使用以下脚本在 CIFAR-10 上使用 ResNet-18 进行ACL 预训练。

# Pre-training stage via ACL
git clone https://github.com/GodXuxilie/Enhancing_ACL_via_AIR.git
cd Enhancing_ACL_via_AIR
PRE_TRAIN_DIR=ACL_ResNet18_cifar10
python pretraining.py $PRE_TRAIN_DIR --dataset cifar10 \
                                     --model r18 \
                                     --DynAug --lambda1 0 --lambda2 0

如何进行微调,来利用预训练模型?

在微调阶段,随机初始化分类器并将其附加到预训练的特征提取器以解决分类任务。微调模式分为三种类型:

  1. 标准线性微调(standard linear fine-tuning, SLF):仅对分类器进行标准微调,同时冻结特征提取器。
  2. 对抗线性微调(adversarial linear fine-tuning, ALF):仅对分类器进行对抗性微调,同时冻结特征提取器。
  3. 对抗完全微调(adversarial full fine-tuning, AFF):对抗性微调特征提取器和分类器。

你可以使用以下脚本通过微调将在 CIFAR-10 上预训练 ResNet-18 迁移到下游任务 CIFAR-100:

# Fine-tuning stage
cd Enhancing_ACL_via_AIR
PRE_TRAIN_DIR=ACL_ResNet18_cifar10
FINETUNE_DIR=ACL_ResNet18_cifar10_cifar100
MODE=SLF/ALF/AFF/ALL
python finetuning.py --mode $MODE \
                     --experiment $FINETUNE_DIR \
                     --checkpoint ./checkpoints/$PRE_TRAIN_DIR/model.pt \
                     --dataset cifar100 \
                     --model r18 \
                     --eval-AA --eval-OOD --pretraining DynACL

[NeurIPS'23] Enhancing Adversarial Contrastive Learning via Adversarial Invariant Regularization 论文导读

从因果分析角度理解ACL

ACL的因果图。

在数据生成过程 (data generation procedure) 期间:

AIR正则化

风格不变标准

AIR的损失函数

为了实现风格不变的标准,AIR正则化是来将特征规范为风格无关,如下所示:

直观上来说,AIR 旨在最大化原始数据 (original data),对抗视角 (adversarial view),以及自然视角 (natural view) 之间的一致性。

AIR 的学习目标

我们的代码公开在GitHub:

https//github.com/GodXuxilie/Enhancing_ACL_via_AIR

你可以使用以下脚本在 CIFAR-10 上使用 ResNet-18 进行 AIR 预训练:

# Pre-training stage via AIR
git clone https://github.com/GodXuxilie/Enhancing_ACL_via_AIR.git
cd Enhancing_ACL_via_AIR
PRE_TRAIN_DIR=AIR_ResNet18_cifar10
python pretraining.py $PRE_TRAIN_DIR --dataset cifar10 --model r18 --DynAug

实验结果

AIR在下游任务的鲁棒性上达到了最佳。

AIR在common corruption下的鲁棒准确率。

如果要复现上述从 CIFAR-10 到 CIFAR-100 的迁移结果,你可以使用以下脚本:

  • 在预训练阶段,使用ResNet-18在CIFAR-10上进行AIR。
# Pre-training stage using AIR
git clone https://github.com/GodXuxilie/Enhancing_ACL_via_AIR.git
cd Enhancing_ACL_via_AIR
PRE_TRAIN_DIR=AIR_ResNet18_cifar10
python pretraining.py $PRETRAIN_DIR --dataset cifar10 --model r18 --DynAug
  • 在微调阶段,你可以微调预训练的 ResNet-18 到下游任务 CIFAR-100. 在微调阶段,以下脚本将自动执行所有三种微调模式(即 SLF、ALF 和 AFF)。 微调阶段结束后,您可以从“$FINETUNE_DIR/results/log.txt”的日志文件中检查标准准确度 (standard accuracy, SA)、在对抗攻击下的鲁棒准确率 (robust accuracy under AutoAttack, AA)以及在common corruption的鲁棒准确率。
cd Enhancing_ACL_via_AIR
PRE_TRAIN_DIR=AIR_ResNet18_cifar10
FINETUNE_DIR=AIR_ResNet18_cifar10_cifar100
python finetuning.py --experiment $EXP_DIR \
                     --checkpoint ./checkpoints/$PRE_TRAIN_DIR/model.pt \
                     --dataset cifar100 \
                     --model r18 \
                     --mode ALL \
                     --eval-AA --eval-OOD --pretraining DynACL_AIR

AIR 在RobustSSL Benchmark上的排行榜排名第一!

有关排行榜的更多信息,请查看https//robustssl.github.io/网站。

[NeurIPS'23 Spotlight] Efficient Adversarial Contrastive Learning via Robustness-Aware Coreset Selection 论文导读

这篇文章提出了鲁棒性感知核心集选择(RCS),它选择一个没有标签注释的信息丰富的核心集来加速 ACL。 从理论上讲,我们证明了贪婪搜索算法可以有效地找到核心集。实验上,我们展示RCS 可以在 CIFAR 和 ImageNet-1K 数据集上大幅加速 ACL 和有监督的鲁棒预训练,而不会显着损害鲁棒性可迁移性。 这篇论文首次证明了在大规模数据集上应用 ACL 的可能性的概念。

动机--- ACL是非常耗时的

ACL 在大规模数据集上的计算量巨大,因为生成对抗性数据需要昂贵的计算开销。

ACL 效率低下,因为 T次PGD迭代需要昂贵的计算开销。

根据实验结果,在 RTX A5000 GPU 上评估整个 ImageNet-1K 数据集(1,281,167 个训练数据点)的 ACL 需要大约 650 小时。由于ACL效率低下,ACL尚未应用于不带RCS的ImageNet-1K数据集。

鲁棒性感知核心集选择(Robustness-Aware Coreset Selection, RCS)

直观的加速想法

RCS 生成信息丰富的核心集,使 ACL 高效地获得有效的鲁棒基础模型。图片来自 https://medium.com/analytics-vidhya/sampling-statistical-approach-in-machine-learning-4903c40ebf86。

通过贪婪搜索进行 RCS

通过 RCS 实现加速 ACL 的算法

通过 RCS 的加速训练ACL。 在预热期(warm-up)结束后,模型将在核心集上进行训练。 因此,RCS 通过减少训练数据的数量使训练过程更加高效。

我们的代码公开在GitHub。

https//github.com/GodXuxilie/Efficient_ACL_via_RCS

实验结果

RCS 显着加快了 CIFAR-10 上的 ACL。

你可以使用以下脚本来复现上述结果---从 CIFAR-10 到 CIFAR-100 的鲁棒可迁移性。

# Pre-training stage using RCS
git clone https://github.com/GodXuxilie/Efficient_ACL_via_RCS.git
cd Efficient_ACL_via_RCS/ACL_RCS/small_scale_datasets
PRE_TRAIN_DIR=ACL_RCS_ResNet18_cifar10
python DynACL_RCS.py $PRE_TRAIN_DIR --ACL_DS --dataset cifar10 --fraction 0.2

# Fine-tuning stage (SLF, ALF, AFF)
cd Efficient_ACL_via_RCS/ACL_RCS/small_scale_datasets
PRE_TRAIN_DIR=ACL_RCS_ResNet18_cifar10
FINETUNE_DIR=ACL_RCS_ResNet18_cifar10_cifar100
python finetuning.py --experiment $FINETUNE_DIR \
                     --checkpoint ./checkpoints/$PRE_TRAIN_DIR/model.pt \
                     --dataset cifar100 \
                     --model r18 \
                     --mode ALL --eval-AA --eval-OOD --pretraining DynACL_RCS

首次通过 RCS 在 ImageNet-1K 上高效地进行了ACL预训练

你可以使用以下脚本来复现上述结果---从 ImageNet-1K 到 CIFAR-10 的鲁棒可迁移性。

# Pre-training stage using RCS
git clone https://github.com/GodXuxilie/Efficient_ACL_via_RCS.git
cd Efficient_ACL_via_RCS/ACL_RCS/ImageNet_32
PRE_TRAIN_DIR=ACL_RCS_WRN_ImageNet
python ACL_RCS.py $PRE_TRAIN_DIR --gpu 0,1,2,3 --ACL_DS --fraction 0.05

# Fine-tuning stage
cd Efficient_ACL_via_RCS/ACL_RCS/ImageNet_32
PRE_TRAIN_DIR=ACL_RCS_WRN_ImageNet
FINETUNE_DIR=ACL_RCS_WRN_ImageNet_cifar10
# Fine-tuning stage (SLF)
python transfer.py --out_dir $FINETUNE_DIR/SLF \
                   --resume $PRE_TRAIN_DIR/model.pt 
                   --dataset cifar10 \
                   --lr 0.01 --linear 
# Fine-tuning stage (ALF)
python adv_tune.py --out_dir $FINETUNE_DIR/ALF \
                   --resume $PRE_TRAIN_DIR/model.pt \
                   --dataset cifar10 \
                   --lr 0.1 --linear 
# Fine-tuning stage (AFF)
python adv_tune.py --out_dir $FINETUNE_DIR/AFF \
                   --resume $PRE_TRAIN_DIR/model.pt \
                   --dataset cifar10 \
                   --lr 0.1

RCS 可以加速 ImageNet-1K 上的标准对抗训练 (standard adversarial training, SAT)。结果表明,RCS 适用于有监督的鲁棒预训练。       

你可以使用以下脚本来复现上述结果---从 ImageNet-1K 到 CIFAR-10 的鲁棒可迁移性。

git clone https://github.com/GodXuxilie/Efficient_ACL_via_RCS.git
cd Efficient_ACL_via_RCS/SAT_RCS/ImageNet_32
# Pre-training stage using RCS
PRE_TRAIN_DIR=SAT_RCS_WRN_ImageNet
nohup python SAT_RCS.py --gpu 0,1,2,3 --out_dir $PRE_TRAIN_DIR --fraction 0.2

# Fine-tuning stage
cd Efficient_ACL_via_RCS/SAT_RCS/ImageNet_32
PRE_TRAIN_DIR=SAT_RCS_WRN_ImageNet
FINETUNE_DIR=SAT_RCS_WRN_ImageNet_cifar10
# Fine-tuning stage (ALF)
python adv_tune.py --out_dir $FINETUNE_DIR/ALF \
                   --resume $PRE_TRAIN_DIR/checkpoint.pth.tar \
                   --dataset cifar10 \
                   --lr 0.1 \
                   --linear 
# Fine-tuning stage (AFF)
python adv_tune.py --out_dir $FINETUNE_DIR/AFF \
                   --resume $PRE_TRAIN_DIR/checkpoint.pth.tar 
                   --dataset cifar10 \
                   --lr 0.1

结语

这两项工作都是由以下作者合作完成:

Xilie Xu 徐曦烈- 新加坡国立大学(http//godxuxilie.github.io)

Jingfeng Zhang 张景锋- 理化学研究所 RIKEN-AIP 奥克兰大学(https//zjfheart.github.io/)

Feng Liu 刘峰- 墨尔本大学(https//fengliu90.github.io/)

Masashi Sugiyama 杉山 将- 理化学研究所 RIKEN-AIP 东京大学(http//www.ms.k.u-tokyo.ac.jp/sugi/profile-jp.html)

Mohan Kankanhalli- 新加坡国立大学(https//www.comp.nus.edu.sg/~mohan/)

参考

1.Bommasani, R., Hudson, D. A., Adeli, E., Altman, R., Arora, S., von Arx, S., ... & Liang, P. (2021). On the opportunities and risks of foundation models. arXiv preprint arXiv:2108.07258.

2.Ban, Y., & Dong, Y. (2022). Pre-trained Adversarial Perturbations. Advances in Neural Information Processing Systems, 35, 1196-1209.

3.Luo, R., Wang, Y., & Wang, Y. (2022, September). Rethinking the Effect of Data Augmentation in Adversarial Contrastive Learning. In The Eleventh International Conference on Learning Representations.

4.https://openreview.net/forum?id=zuXyQsXVLF

5.https://openreview.net/forum?id=fpzA8uRA95

6.Brown, T., Mann, B., Ryder, N., Subbiah, M., Kaplan, J. D., Dhariwal, P., ... & Amodei, D. (2020). Language models are few-shot learners. Advances in neural information processing systems, 33, 1877-1901.

7.Dosovitskiy, A., Beyer, L., Kolesnikov, A., Weissenborn, D., Zhai, X., Unterthiner, T., ... & Houlsby, N. (2020). An image is worth 16x16 words: Transformers for image recognition at scale. arXiv preprint arXiv:2010.11929.

8.Li, J., Li, D., Xiong, C., & Hoi, S. (2022, June). Blip: Bootstrapping language-image pre-training for unified vision-language understanding and generation. In International Conference on Machine Learning (pp. 12888-12900). PMLR.

9.Elizalde, B., Deshmukh, S., Al Ismail, M., & Wang, H. (2023, June). Clap learning audio concepts from natural language supervision. In ICASSP 2023-2023 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP) (pp. 1-5). IEEE.

10.He, K., Gan, C., Li, Z., Rekik, I., Yin, Z., Ji, W., ... & Shen, D. (2023). Transformers in medical image analysis. Intelligent Medicine, 3(1), 59-78.

11.Eykholt, K., Evtimov, I., Fernandes, E., Li, B., Rahmati, A., Xiao, C., ... & Song, D. (2018). Robust physical-world attacks on deep learning visual classification. In Proceedings of the IEEE conference on computer vision and pattern recognition (pp. 1625-1634).

12.Chen, T., Kornblith, S., Norouzi, M., & Hinton, G. (2020, November). A simple framework for contrastive learning of visual representations. In International conference on machine learning (pp. 1597-1607). PMLR.

13.Madry, A., Makelov, A., Schmidt, L., Tsipras, D., & Vladu, A. (2017). Towards deep learning models resistant to adversarial attacks. arXiv preprint arXiv:1706.06083.

14.实际上,我们严格证明了一个代理集合函数是weakly submodular。 进一步,我们利用这个代理集合函数,来证明了所提出的贪婪搜索算法为集函数最大化问题提供了有保证的下界。 更多细节请参考我们的RCS论文。

#MUDDFormer

Transformer还有救?魔改残差连接,MUDDFormer硬刚超深网络

将原单一残差流拆为 Q/K/V/R 四路动态稠密跨层连接,仅用 0.23% 额外参数即可让 2.8 B 模型在零样本和 5-shot 任务上分别追平 6.9 B 和 12 B 的 Pythia,深度扩展至 42 层仍持续增长,显著缓解表征坍塌与残差流信息过载。

当下大多数人都觉得 Transformer 已经被研究得差不多了,想在结构上做出能超越它的创新,基本不太可能了。

但是总有人愿意打破常规,最近一篇 ICML 2025 的论文,又让 Transformer 有了新活力。它把目光放在了 Transformer 里一个我们平时没太在意、甚至有点忽略的部分——残差连接。

残差连接(residual connections)自何恺明在 2015 年开山之作 ResNet [1] 中提出后,就成为深度学习乃至 Transformer LLMs 的一大基石。但在当今的深度 Transformer LLMs 中仍有其局限性,限制了信息在跨层间的高效传递。

彩云科技与北京邮电大学近期联合提出了一个简单有效的残差连接替代:多路动态稠密连接(MUltiway Dynamic Dense (MUDD) connection),大幅度提高了 Transformer 跨层信息传递的效率。

图片

论文标题:

MUDDFormer: Breaking Residual Bottlenecks in Transformers via Multiway Dynamic Dense Connections

论文链接:

​https://arxiv.org/abs/2502.12170​

代码链接:

​https://github.com/Caiyun-AI/MUDDFormer​

模型链接:

​https://huggingface.co/Caiyun-AI/MUDDFormer-2.8B​

​https://huggingface.co/Caiyun-AI/MUDDPythia-1.4B​

​https://huggingface.co/Caiyun-AI/MUDDPythia-2.8B​

大规模语言模型预训练实验表明,仅增加 0.23% 的参数量和 0.4% 的计算量,采用该架构的 2.8B 参数量 MUDDPythia 模型即可在 0-shot 和 5-shot 评估中分别媲美 6.9B 参数量(~2.4 倍)和 12B 参数量(~4.2 倍)的 Pythia 模型,表明了 MUDD 连接对 Transformer 的基础能力(尤其是上下文学习能力)的显著提升。

这是该团队继 DCFormer [2](ICML 2024)后又一项大模型底层架构创新工作,已被 ICML 2025 接收,论文、代码和模型权重均已公开。

01 背景

在 Transformer 中残差流汇集了多层的信息,同时也为 Attention 和 FFN 提供多路信息,比如在 Attention 模块中需要获取 query、key、value 三路信息,残差流本身也可以看作一路信息流(记作 R)。

虽然残差连接的引入首次让训练超深度网络成为可能,但在当今的深度 Transformer LLMs 中仍有其局限:

深层隐状态的表征坍塌(Representation Collapse):目前 Transformer LLM 普遍采用 Pre-Norm 训练,多个理论和实证工作 [3,4] 表明,当达到一定层深后,再增加更多的层会出现边际效应递减,相邻层的隐状态表征高度相似(即「表征坍塌」),让参数和算力 scaling 的效果大打折扣。

残差流的信息过载:Transformer 机制可解释性研究表明,跨越不同层的注意力头和前馈网络通过读写残差流(residual stream)交互组成回路(circuit),对模型的上下文学习(in-context learning)等能力至关重要。
在非常深的 Transformer 模型中,残差流作为多层间「通信总线」可能因为多路信息共享残差流,以及多层信息在深层汇集而「超载」成为瓶颈,妨碍形成解决困难任务所必须的复杂回路。

针对上述局限,MUDD 根据当前隐状态动态搭建跨层连接(可视为深度方向的多头注意力),来缓解深层隐状态的表征坍塌,同时针对 Transformer 每层的 query、key、value、残差等不同输入流采用各自的动态连接,来减少多路信息流的相互干扰,缓解残差流的信息过载,这样既大幅度拓宽了跨层信息传输带宽,又保证了非常高的参数和计算效率。

02 核心架构

如图 1a 所示,为了实现更直接的跨层交互,DenseNet [5] 将当前 Block 和前面所有的 Block 进行稠密连接(Dense Connectivity)。

最近 Pagliardini 等人 [6] 将其引入 Transformer,提出了 DenseFormer(NeurIPS 2025),如图 1b 所示。

它通过一组可学习但静态的权重(如 w_i,j)来加权求和前面所有层的输出。这种方式虽然拓宽了信息通路,但静态权重使得对于序列中不同 token 都做同样处理,限制了表达能力。

▲ 图1. MUDD 的架构图

▲ 图1. MUDD 的架构图

研究者首先提出了 DynamicDenseFormer(如图 1c),用  表示第个 token 的跨层信息聚合模块(Depth-wise Aggregate),连接权重不再是固定的参数,而是由当前层的隐状态  动态生成的一个权重矩阵  。

这意味着,模型可以根据每个 token 的上下文语境,自适应地决定应该从前面的哪一层、以多大的权重提取信息。本质上,这可以看作是在深度维度上的一次单头注意力,与 Vaswani 等人 [7] 在 Transformer 中提出的 token 维度上的注意力机制遥相呼应。

图片

在 DynamicDenseFormer 的基础上引入多路连接就得到了 MUDDFormer,如图 1d 所示,它将原本单一的动态连接,解耦成四路独立的动态连接,分别为 Q、K、V、R(图 1d 中的等)定制各自的跨层信息聚合模块。

这样每一路信息流都可以根据自己的需求,更高效地从前面层中获取对应信息。可看作是深度向的多头注意力

图片

02 实验评估

Scaling Law

▲ 图2. MUDDFormer和基线模型的扩展实验

▲ 图2. MUDDFormer和基线模型的扩展实验

研究者在 Pile 数据集上测试了 MUDDFormer 和其他基线模型的扩展能力,如图 2 所示。

Hyper-Connections [8] 也是字节跳动 Seed 最近一个发表在 ICLR 2025 的改进残差连接的工作,图 2 中可见 DynamicDenseFormer 已经比 DenseFormer 和 Hyper-Connections 都表现好,而且在解耦多路信息流后,MUDDFormer 又有明显的效果提升。

在所有模型尺寸下 MUDDFormer 都显著领先 Transformer++ 和其他基线模型 (Loss 越低越好),并且其领先优势随着模型增大并未减小。MUDDFormer-834M 的性能,已经超越了需要 1.89 倍计算量的 Transformer++ 基线模型,展现了惊人的计算效率提升。

▲ 图3. MUDDFormer和Transformer++的深度扩展实验

▲ 图3. MUDDFormer和Transformer++的深度扩展实验

为了验证 MUDDFormer 在更深层模型上的有效性,研究者在不增加参数量的前提下增加模型的深度,并进行了扩展实验,如图 3。

Transformer++ 在超过 24 层后收益递减(缩放曲线几乎重合),而 deep MUDDFormer 在高达 42 层时仍能保持收益,使得在 797M 下达到了 2.08 倍 Transformer++ 的性能。这进一步验证了 MUDD 连接可以通过增强跨层信息流来缓解深度引起的瓶颈。​

下游任务测评

研究者将 MUDD 架构与开源的 Pythia 模型框架结合,在 300B tokens 的数据上进行训练,并与从 1.4B 到 12B 的全系列 Pythia 模型进行比较,如图 4。

▲ 图4. 下游任务对比测评

▲ 图4. 下游任务对比测评

首先,MUDDPythia 在 0-shot 和 5-shot 任务上的平均准确率,都明显高于同等计算量下的 Pythia 模型,而且在 5-shot 下的提升效果更明显,说明上下文能力得到了额外的增强。

从图 5 中可以看出在 0-shot 下,2.8B 的 MUDDPythia 的性能媲美了 6.9B 的 Pythia,实现了 2.4 倍的计算效率飞跃;在 5-shot 下,2.8B 的 MUDDPythia 的性能,甚至追平了 12B 的 Pythia,实现了 4.2 倍计算效率提升!

▲ 图5. 下游任务准确率对比曲线

▲ 图5. 下游任务准确率对比曲线

这表明,MUDD 所构建的高效信息通路,极大地增强了模型在上下文中动态构建复杂推理回路的能力。​

分析

图 6 展示了模型注意力头激活比例随层数的变化,在标准的 Pythia 模型中,随着层数加深大量注意力头都只关注少数几个 token(attention sink [9])并未激活。

▲ 图6. 注意力头激活比例的逐层变化曲线

▲ 图6. 注意力头激活比例的逐层变化曲线

然而,在 MUDDPythia 中,几乎在所有层的注意力头激活率都远高于 Pythia,平均高出约 2.4 倍。这说明 MUDD 连接加强了对 Attention 的利用,也部分解释了上下文能力的增强。​

04 结语

MUDDFormer 通过简单高效的实现改进了残差连接,为 Transformer 内部不同的信息流(Q、K、V、R)建立各自独立的动态跨层连接,不仅增强了 Transformer 模型的跨层交互,而且进一步提升了模型的上下文学习能力。

实验证明这种新的连接机制能以微弱的代价,换来模型性能和计算效率的巨大飞跃。MUDDFormer 所展示的潜力,使其有望成为下一代基础模型架构中不可或缺的新基石。

参考文献

[1] He, K., Zhang, X., Ren, S., and Sun, J. Deep residual learning for image recognition. In Proceedings of the IEEE conference on computer vision and pattern recognition (CVPR), pp. 770–778, 2016.

[2] Xiao, Da, et al. "Improving transformers with dynamically composable multi-head attention." Proceedings of the 41st International Conference on Machine Learning. 2024.

[3] Liu, L., Liu, X., Gao, J., Chen, W., and Han, J. Understanding the difficulty of training transformers. In Proceedings of the 2020 Conference on Empirical Methods in Natural Language Processing (EMNLP), 2020b.

[4] Gromov, A., Tirumala, K., Shapourian, H., Glorioso, P., and Roberts, D. A. The unreasonable ineffectiveness of the deeper layers. arXiv preprint arXiv:2403.17887, 2024.

[5] Huang, G., Liu, Z., Van Der Maaten, L., and Weinberger, K. Q. Densely connected convolutional networks. In Proceedings of the IEEE conference on computer vision and pattern recognition (CVPR), pp. 4700–4708, 2017.

[6] Pagliardini, M., Mohtashami, A., Fleuret, F., and Jaggi, M. Denseformer: Enhancing information flow in transformers via depth weighted averaging. In Proceedings of the Thirty-Eighth Annual Conference on Neural Information Processing Systems (NeurIPS), 2024.

[7] Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., Kaiser, Ł., and Polosukhin, I. Attention is all you need. Advances in neural information processing systems, 30, 2017.

[8] Zhu, D., Huang, H., Huang, Z., Zeng, Y., Mao, Y., Wu, B., Min, Q., and Zhou, X. Hyper-connections. In Proceedings of the Thirteenth International Conference on Learning Representations (ICLR), 2025

[9] Xiao, G., Tian, Y., Chen, B., Han, S., and Lewis, M. Efficient streaming language models with attention sinks. In The Twelfth International Conference on Learning Representations (ICLR), 2024b.

#Local Dense Relational Logit Distillation, LDRLD

不止于模仿:LDRLD深挖Logit内部关系,开启知识蒸馏新篇章

知识蒸馏(Knowledge Distillation, KD)作为一种高效的模型压缩和优化技术,其核心思想在于让一个强大的“教师”网络,将其所学到的“知识”传授给一个轻量的“学生”网络。其中,基于Logits(模型在最终输出概率前的原始得分)的蒸馏方法因其简洁、高效而备受青睐。然而,这些方法大多停留在让学生模仿教师整体Logits分布的层面,却忽略了Logits中蕴含的、更为丰富的细粒度类别关系。

为了挖掘这部分被忽视的“暗知识”,来自东北大学、华南师范大学等机构的研究者们提出了局部密集关系Logits蒸馏(Local Dense Relational Logit Distillation, LDRLD),这是一种新颖的蒸馏方法,它通过递归地解耦和重组Logits信息,迫使学生网络学习类别之间更精细、更清晰的内在关联,从而在多个基准测试上取得了超越SOTA的性能。

  • 论文标题: Local Dense Logit Relations for Enhanced Knowledge Distillation
  • 作者: Liuchi Xu, Kang Liu, Jinshuai Liu, Lu Wang, Lisheng Xu, Jun Cheng
  • 机构: 东北大学;华南师范大学;中国科学院;香港中文大学
  • 论文地址:​​ https://arxiv.org/pdf/2507.15911v1​​
  • 会议/期刊: 已被 ICCV 2025 接收

核心思想:从全局模仿到局部关系解耦

传统的知识蒸馏(KD)方法通常使用一个全局的Softmax函数来软化教师的Logits,这虽然保留了类别间的相对大小关系(例如,教师认为图片是“猫”的可能性比“汽车”大),但也将所有类别的Logits耦合在一起计算概率,这在一定程度上“稀释”了那些最相似、最易混淆类别之间的细微差别。

LDRLD的核心洞察在于:真正有价值的知识,往往隐藏在那些最相似的类别对(category pairs)的关系中。如下图所示,“猫”和“狗”之间的区别是通过概率分布反映出来的,但引入无关的(例如“汽车”)可能会干扰和削弱它们的类间可辨性。

这些限制共同阻碍了学生捕捉独特类别特征的能力,可能会降低其表现。因此,在综合利用logit关系的同时,提高类间可辨性至关重要。

图片

为了实现这一目标,LDRLD设计了一个包含三大核心组件的精巧框架:

图片

1.  递归解耦与重组 (Recursive Decoupling and Recombination):

这是LDRLD的基石。它不再一次性蒸馏所有类别的Logits,而是递归地、成对地解耦Logits信息。在每一层递归中,它都聚焦于当前最相似的类别对,提取它们之间的关系知识,然后将剩余的知识传入下一层递归,直至所有关系被充分挖掘。

2. 自适应衰减权重 (Adaptive Decay Weight, ADW):

为了让模型更关注那些最重要的类别关系,研究者引入了ADW策略。它包含两个子策略:

  • 逆序权重 (Inverse Rank Weighting, IRW): 根据教师Logits的排名,为排名更接近的类别对赋予更高的权重。例如,如果教师认为最可能的是“猫”,其次是“狗”,那么“猫-狗”这对关系的学习权重就应该很高。
  • 指数秩衰减 (Exponential Rank Decay, ERD): 根据类别对的整体排名分数,自适应地控制权重衰减,确保模型不会在那些教师认为完全不相关的类别上花费过多精力。

3. 剩余知识蒸馏 (Distilling Remaining Knowledge):

在递归地提取完成类别对的关系知识后,为了保证知识的完整性,LDRLD还会将剩余的非目标知识进行一次整体蒸馏,确保学生不会丢失全局信息。

实验结果:全面超越SOTA

在CIFAR-100、ImageNet-1K和Tiny-ImageNet等多个标准数据集上,LDRLD展现了其卓越的性能。

如下表所示,无论教师和学生的网络架构相同还是不同,LDRLD在Top-1准确率上均显著优于包括经典KD、DKD、MLKD在内的多种SOTA Logits蒸馏方法。

图片

图片

图片

图片

特征图可视化也直观地证明了LDRLD的有效性。如下图所示,使用LDRLD训练的学生网络,其倒数第二层的特征图展现出比使用传统KD方法训练的学生网络更高的类内紧凑性和类间可分性,这意味着学生学到了更具辨识力的特征。

图片

为了验证该方法的泛化能力,将任务扩展到人Re-ID。如下表所示,结果表明,与其他蒸馏方法(如AT和DKD)相比,所提出方法表现出更优的性能,证实了其有效性。

图片

论文贡献与价值

  • 提出LDRLD新方法: 提出通过递归解耦和重组Logits来挖掘和传递细粒度类别关系知识的新范式,为知识蒸馏领域提供了新的视角。
  • 设计ADW策略: 创新的自适应衰减权重策略,能够动态地聚焦于最关键的知识进行传递,显著提升了蒸馏效率和效果。
  • 性能SOTA: 在多个基准测试中取得了全面领先的性能,为知识蒸馏领域设立了新的性能标杆。
  • 代码即将开源: 研究团队承诺将公开代码,这将极大地促进社区对细粒度知识蒸馏的研究。

总而言之,LDRLD不再满足于让学生模仿教师的“最终答案”,而是通过一种更精细的方式,引导学生去学习教师在得出答案过程中的“思考逻辑”和“权衡过程”,为训练出更强大的轻量级模型提供了一条极具潜力的新路径。

#Diffusion/VAE/RL 数学原理

最近做的两个project,一个训VAE,一个训RL都进行project尾声了,但是对于数学原理上的掌握还很浅

看到了这个up的slide做的非常不错,在这里结合自己的理解分享一下

【大白话01】一文理清 Diffusion Model 扩散模型 | 原理图解+公式推导_哔哩哔哩_bilibili​

Diffusion

原图提供gaussian distribution的mean value,noise系数提供Gaussian distribution的variance value

​!$\sqrt{\alpha_t-\alpha_t\alpha_{t-1}}\epsilon_{t-2}\sim N(0,\alpha_t-\alpha_t\alpha_{t-1}) \\ \sqrt{1- \alpha_t}\epsilon_{t-1}\sim N(0,1- \alpha_t) \\ \sqrt{1- \alpha_t}\epsilon_{t-1}+\sqrt{\alpha_t-\alpha_t\alpha_{t-1}}\epsilon_{t-2}\sim N(0,1-\alpha_t\alpha_{t-1}) $​

假设每一步denoise都满足gaussian distribution,所以network训练目标是拟合这两个gaussian distribution的mean&variance

先忽略推导,只关注KL项

中间这个KL项就是在拟合denoise中理论值和network预测值

虽然  ​​!$q(x_{t-1}|x_t)$​​​ 是不确定的,但是  ​​!$q(x_{t-1}|x_t,x_0)$​​ 是个确定的过程

这里的推导比较复杂,化简的结果就是  ​​!$x_0$​​ 是未知的

将本来不确定的  ​​!$x_0$​​​  转为不确定的  ​​!$\epsilon$​​  ,迭代预测noise

最后这里就没啥好说的了,将拟合两个distribution转为拟合两个noise​

VAE

VAE假设latent distribution是个gaussian distribution

AE是没有生成能力的

用神经网络拟合encoder

最小化KL loss,相当于要最大化ELBO

logp(x)相当于是在原始数据集的分布,是确定的

为了避免latent space退化为variance=0的尖锐分布,无法学出gaussian distribution

通过假设p(z|x)即encode处满足gaussian distribution

将VAE的训练转为重建loss+KL约束loss

training&sampling

reconstruction loss & kl loss的PCA消融实验

补充一下表征这件事情

  1. semantic representation:希望它趋近于脉冲分布
  2. generate representation:希望它趋近于Gaussian分布

原因:希望对generate representation进行插值,但是semantic representation希望它进行classification​

RL

markov decision process就是(s1,a1,s2,a2...)

结合马尔可夫的图,分清楚在哪个点上是action value,哪个点上是state value

temporal difference:往前多走一步,用更准确的结果更新过去的估计

policy gradient:让network去学习怎么样在知道s的情况下,s-->a可以取到最合适的转换

#抛开数学,轻松学懂VAE

本文作者用“编码成一条线”的直觉比喻,带你绕过数学推导,10 分钟看懂 VAE 如何把自编码器的点变成分布、用 KL 散度防过拟合,并给出 PyTorch 从零训练人脸生成的完整代码。

变分自编码器(VAE)是一类常见的生成模型。纯VAE的生成效果不见得是最好的,但VAE还是经常会被用作大模型的子模块。即使是在VAE发明多年的今天,学习VAE还是很有必要的。相比GAN等更符合直觉的模型,彻底理解VAE对数学的要求较高。在这篇文章中,我会从计算机科学的角度出发,简明地讲清楚VAE的核心原理,并附上代码实现的介绍。同时,我会稍微提及VAE是怎么利用数学知识的,以及该怎么去拓展了解这些数学知识。​

用自编码器生成图像

在正式开始学习VAE之前,我们先探讨一下内容生成的几种方式,并引入自编码器(Autoencoder, AE)这个概念。为了方面描述,我们仅讨论图像的生成。

在设计生成图像的程序之前,我们要考虑一个问题——程序的输入是什么?如果程序没有任何输入,那么它就应该有一个确定的输出,也就是只能画出一幅图片。而只能画出一幅图片的程序没有任何意义的。因此,一个图像生成模型一定要有输入,用于区分不同的图片。哪怕这种输入仅仅是0, 1, 2这种序号也可以,只要模型能看懂输入,为每个输入生成不同的图片就行了。

可是,我们不仅希望不同的输入能区分不同的图片,还要让相近的输入生成相近的图片。比如1.5号图片应该长得和1号和2号相似。为了让模型满足这种性质,我们可以干脆把模型的输入建模成有意义的高维实数向量。这个向量,可以是看成对图像的一种压缩编码。比如(170, 1)就表示一幅身高为170cm的男性的照片。

绝大多数生成模型都是用这种方式对生成过程建模。所有的输入向量  来自于一个标准正态分布  。图像生成,就是把图像的编码向量  解码成一幅图像的过程。不同的生成模型,只是对这个过程有着不同的约束方式。

自编码器的约束方式十分巧妙:既然把  翻译回图像是一个解码的过程,为什么不可以把编码的过程也加进来,让整个过程自动学习呢?如下图所示,我们可以让一个模型(编码器)学会怎么把图片压缩成一个编码,再让另一个模型(解码器)学会怎么把编码解压缩成一幅图片,最小化生成图片与原图片之间的误差。

最后,解码器就是我们需要的生成模型。只要在标准多元正态分布里采样出  ,就可生成图片了。另外,理想情况下, 之间的插值向量也能代表在语义上插值的图片。

可是,由于自编码器本身的限制,这种理想不一定能实现。​

自编码器的问题——过拟合

自编码器的信息压缩能力十分强大。只要编码器和解码器的神经网络足够复杂,所有训练集里的图像都可以被压缩成非常短的编码。这种编码短到什么程度了呢?——只要一个一维向量(实数)就可以描述所有训练集里的图像了。

想做到这一点并不难。还记得我们开头对生成模型的输入的讨论吗?只要让模型把所有图片以数组的形式存到编码器和解码器里,以0, 1, 2这样的序号来表示每一幅训练集里的图片,就能完成最极致的信息压缩。当然,使用这种方式的话,编码就失去了所有的语义信息,编码之间的插值也不能表示图像语义上的插值了。

这是由模型过拟合导致的。如果仅使用自编码器本身的约束方式,而不加入其他正则化方法的话,一定会出现过拟合。​

VAE——一种正则化的自编码器

VAE就是一种使用了某种正则化方法的自编码器,它解决了上述的过拟合问题。VAE使用的这种方法来自于概率论的变分推理,不过,我们可以在完全不了解变分推理的前提下看懂VAE。

VAE的想法是这样的:我们最终希望得到一个分布,或者说一条连续的直线。可是,编码器每次只能把图片编码成一个向量,也就是一个点。很多点是很难重建出一条连续的直线的。既然如此,我们可以把每张图片也编码成一个分布。多条直线,就可以比较容易地拼成我们想要的直线了。

当然,只让模型去拟合分布是不够的。如果各个分布都乱七八糟,相距甚远,那么它们怎么都拼不成一个标准正态分布。因此,我们还需要加上一个约束,让各个分布和标准正态分布尽可能相似。

这样,我们可以总结一下VAE的训练框架。VAE依然使用了编码器-解码器的架构。只不过,编码器的输出是一个可学习的正态分布。对分布是不可能做求导和梯度下降的,但我们可以去分布里采样,对采样出来的编码解码并求导。

另外,VAE的损失函数除了要最小化重建图像与原图像之间的均方误差外,还要最大化每个分布和标准正态分布之间的相似度。

常见的描述分布之间相似度的指标叫做KL散度。只要把KL散度的公式套进损失函数里,整个训练框架就算搭好了。

VAE的原理其实就是这么简单。总结一下,VAE本身是一个编码器-解码器结构的自编码器,只不过编码器的输出是一个分布,而解码器的输入是该分布的一个样本。另外,在损失函数中,除了要让重建图像和原图像更接近以外,还要让输出的分布和标准正态分布更加接近。​

VAE 与变分推理

前几段其实只对VAE做了一个直觉上的描述,VAE的损失函数实际上是经严谨的数学推导得到的。如果你对数学知识不感兴趣,完全可以跳过这一节的讲解。当然,这一节也只会简单地描述VAE和变分推理的关系,更详细的数学推导可以去参考网上的其他文章。

让我们从概率论的角度看待生成模型。生成模型中的  可以看成是隐变量,它决定了能观测到的变量  。比如说,袋子里有黑球和白球,你不断地从袋子里取球出来再放回去,就能够统计出抽到黑球和白球的频率。然而,真正决定这个频率的,是袋子里黑球和白球的数量,这些数量就是观测不到的隐变量。简单来说,隐变量  是因,变量  是果。

生成模型,其实就是假设  来自标准正态分布,想要拟合分布 (解码器),以得到  的分布(图像分布)。为了训练解码器,自编码器架构使用了一个编码器以描述  。这样,从训练集里采样,等于是采样出了一个  。根据  求出一个  ,再根据  试图重建  。优化这个过程,就是在优化编码器和解码器,也就是优化  和  。

然而, 和  之间有一个约束,它们必须满足贝叶斯公式:

假如我们要用一个和  有关的关于  的分布  去拟合  ,就要让  和  这两个分布尽可能相似。如果这个相似度是KL散度,经过一系列的推导,就可以推导出我们在 VAE里使用的那个损失函数。

简单来说,拟合一个未知分布的技术就叫做变分推理。VAE利用变分推理,对模型的编码器和解码器加了一个约束,这个约束在化简后就是VAE的损失函数。

VAE和变分推理的关系就是这样。如果还想细究,可以去先学习KL散度相关的知识,再去看一下VAE中KL散度的公式推导。当然,不懂这些概念并不影响VAE的学习。​

总结

VAE其实就是一个编码器-解码器架构,和U-Net以及部分NLP模型类似。然而,为了抑制自编码过程中的过拟合,VAE编码器的输出是一个正态分布,而不是一个具体的编码。同时,VAE的损失函数除了约束重建图像外,还约束了生成的分布。在这些改进下,VAE能够顺利地训练出一个解码器,以把来自正态分布的随机变量画成一幅图像。如果你想通过代码实践进一步加深对VAE的理解,可以阅读附录。​

参考资料

  1. 一篇不错的VAE讲解。我是跟着这篇文章学习的。https://towardsdatascience.com/understanding-variational-autoencoders-vaes-f70510919f73
  2. 我的VAE PyTorch实现参考了这个仓库:https://github.com/AntixK/PyTorch-VAE 。开头的人脸生成效果图是从这个项目里摘抄过来的。​

VAE PyTorch 实现

项目网址:https://github.com/SingleZombie/DL-Demos/tree/master/dldemos/VAE​

数据集

在这个项目中,我使用了CelebA数据集。这个数据集有200k张人脸,裁剪和对齐后的图片只有1个多G,对实验非常友好。

CelebA的下载链接可以在官方网站上找到:https://mmlab.ie.cuhk.edu.hk/projects/CelebA.html。

下载好了图片后,可以用下面的代码创建Dataloader。

import os

import torch
from PIL import Image
from torch.utils.data import DataLoader, Dataset
from torchvision import transforms


class CelebADataset(Dataset):
    def __init__(self, root, img_shape=(64, 64)) -> None:
        super().__init__()
        self.root = root
        self.img_shape = img_shape
        self.filenames = sorted(os.listdir(root))

    def __len__(self) -> int:
        return len(self.filenames)

    def __getitem__(self, index: int):
        path = os.path.join(self.root, self.filenames[index])
        img = Image.open(path).convert('RGB')
        pipeline = transforms.Compose([
            transforms.CenterCrop(168),
            transforms.Resize(self.img_shape),
            transforms.ToTensor()
        ])
        return pipeline(img)


def get_dataloader(root='data/celebA/img_align_celeba', **kwargs):
    dataset = CelebADataset(root, **kwargs)
    return DataLoader(dataset, 16, shuffle=True)

这段代码是一段非常常规的根据图片路径读取图片的代码。只有少数地方需要说明:

  • 为了尽快完成demo,所有人脸图片的分辨率都是。
  • CelebA里裁剪后的人脸图片是长方形的。要先调用​​CenterCrop​​裁剪出正方形人脸,再做Resize。

为了验证Dataloader的正确性,我们可以写一些脚本来查看Dataloader里的一个batch的图片。

if __name__ == '__main__':
    dataloader = get_dataloader()
    img = next(iter(dataloader))
    print(img.shape)
    # Concat 4x4 images
    N, C, H, W = img.shape
    assert N == 16
    img = torch.permute(img, (1, 0, 2, 3))
    img = torch.reshape(img, (C, 4, 4 * H, W))
    img = torch.permute(img, (0, 2, 1, 3))
    img = torch.reshape(img, (C, 4 * H, 4 * W))
    img = transforms.ToPILImage()(img)
    img.save('work_dirs/tmp.jpg')

这段代码使用了一些小技巧。首先,​​next(iter(dataloader))​​可以访问Dataloader的第一个数据。其次,在把一个batch的图片转换成图片方格的过程中,我使用了比较骚的换维度、换形状操作,看起来很帅。

模型

我的VAE模型使用了类似U-Net的操作:编码器用卷积把图像的边长减半,通道翻倍,解码器用反卷积把图像的边长翻倍,通道减半。

模型结构的定义函数如下:

import torchimport torch.nn as nnclass VAE(nn.Module):    '''    VAE for 64x64 face generation. The hidden dimensions can be tuned.    '''    def __init__(self, hiddens=[16, 32, 64, 128, 256], latent_dim=128) -> None:        super().__init__()        # encoder        prev_channels = 3        modules = []        img_length = 64        for cur_channels in hiddens:            modules.append(                nn.Sequential(                    nn.Conv2d(prev_channels,                              cur_channels,                              kernel_size=3,                              stride=2,                              padding=1), nn.BatchNorm2d(cur_channels),                    nn.ReLU()))            prev_channels = cur_channels            img_length //= 2        self.encoder = nn.Sequential(*modules)        self.mean_linear = nn.Linear(prev_channels * img_length * img_length,                                     latent_dim)        self.var_linear = nn.Linear(prev_channels * img_length * img_length,                                    latent_dim)        self.latent_dim = latent_dim        # decoder        modules = []        self.decoder_projection = nn.Linear(            latent_dim, prev_channels * img_length * img_length)        self.decoder_input_chw = (prev_channels, img_length, img_length)        for i in range(len(hiddens) - 1, 0, -1):            modules.append(                nn.Sequential(                    nn.ConvTranspose2d(hiddens[i],                                       hiddens[i - 1],                                       kernel_size=3,                                       stride=2,                                       padding=1,                                       output_padding=1),                    nn.BatchNorm2d(hiddens[i - 1]), nn.ReLU()))        modules.append(            nn.Sequential(                nn.ConvTranspose2d(hiddens[0],                                   hiddens[0],                                   kernel_size=3,                                   stride=2,                                   padding=1,                                   output_padding=1),                nn.BatchNorm2d(hiddens[0]), nn.ReLU(),                nn.Conv2d(hiddens[0], 3, kernel_size=3, stride=1, padding=1),                nn.ReLU()))        self.decoder = nn.Sequential(*modules)

首先来看编码器的部分。每个卷积模块由卷积、BN、ReLU构成。卷完了再用两个全连接层分别生成正态分布的均值和方差。注意,卷积完成后,图像的形状是​​[prev_channels, img_length, img_length]​​,为了把它输入到全连接层,我们到时候会做一个flatten操作。

# encoder
        prev_channels = 3
        modules = []
        img_length = 64
        for cur_channels in hiddens:
            modules.append(
                nn.Sequential(
                    nn.Conv2d(prev_channels,
                              cur_channels,
                              kernel_size=3,
                              stride=2,
                              padding=1), nn.BatchNorm2d(cur_channels),
                    nn.ReLU()))
            prev_channels = cur_channels
            img_length //= 2
        self.encoder = nn.Sequential(*modules)
        self.mean_linear = nn.Linear(prev_channels * img_length * img_length,
                                     latent_dim)
        self.var_linear = nn.Linear(prev_channels * img_length * img_length,
                                    latent_dim)
        self.latent_dim = latent_dim

解码器和编码器的操作基本完全相反。由于隐变量的维度是​​latent_dim​​​,需要再用一个全连接层把图像的维度投影回​​[prev_channels, img_length, img_length]​​。之后就是反卷积放大图像的过程。写这些代码时一定要算好图像的边长,定好反卷积的次数,并且不要忘记最后把图像的通道数转换回3。

# decoder
modules = []
self.decoder_projection = nn.Linear(
    latent_dim, prev_channels * img_length * img_length)
self.decoder_input_chw = (prev_channels, img_length, img_length)
for i in range(len(hiddens) - 1, 0, -1):
    modules.append(
        nn.Sequential(
            nn.ConvTranspose2d(hiddens[i],
                                hiddens[i - 1],
                                kernel_size=3,
                                stride=2,
                                padding=1,
                                output_padding=1),
            nn.BatchNorm2d(hiddens[i - 1]), nn.ReLU()))
modules.append(
    nn.Sequential(
        nn.ConvTranspose2d(hiddens[0],
                            hiddens[0],
                            kernel_size=3,
                            stride=2,
                            padding=1,
                            output_padding=1),
        nn.BatchNorm2d(hiddens[0]), nn.ReLU(),
        nn.Conv2d(hiddens[0], 3, kernel_size=3, stride=1, padding=1),
        nn.ReLU()))
self.decoder = nn.Sequential(*modules)

网络前向传播的过程如正文所述,先是用编码器编码,把图像压平送进全连接层得到均值和方差,再用​​randn_like​​​随机采样,把采样的​​z​​投影、变换成正确的维度,送入解码器,最后输出重建图像以及正态分布的均值和方差。

def forward(self, x):
    encoded = self.encoder(x)
    encoded = torch.flatten(encoded, 1)
    mean = self.mean_linear(encoded)
    logvar = self.var_linear(encoded)
    eps = torch.randn_like(logvar)
    std = torch.exp(logvar / 2)
    z = eps * std + mean
    x = self.decoder_projection(z)
    x = torch.reshape(x, (-1, *self.decoder_input_chw))
    decoded = self.decoder(x)

    return decoded, mean, logvar

用该模型随机生成图像的过程和前向传播的过程十分类似,只不过来自于标准正态分布而已,解码过程是一模一样的。

def sample(self, device='cuda'):
    z = torch.randn(1, self.latent_dim).to(device)
    x = self.decoder_projection(z)
    x = torch.reshape(x, (-1, *self.decoder_input_chw))
    decoded = self.decoder(x)
    return decoded

主函数

在主函数中,我们要先完成模型训练。在训练前,还有一件重要的事情要做:定义损失函数。

from time import time

import torch
import torch.nn.functional as F
from torchvision.transforms import ToPILImage

from dldemos.VAE.load_celebA import get_dataloader
from dldemos.VAE.model import VAE

# Hyperparameters
n_epochs = 10
kl_weight = 0.00025
lr = 0.005


def loss_fn(y, y_hat, mean, logvar):
    recons_loss = F.mse_loss(y_hat, y)
    kl_loss = torch.mean(
        -0.5 * torch.sum(1 + logvar - mean**2 - torch.exp(logvar), 1), 0)
    loss = recons_loss + kl_loss * kl_weight
    return loss

如正文所述,VAE的loss包括两部分:图像的重建误差和分布之间的KL散度。二者的比例可以通过​​kl_weight​​来控制。

KL散度的公式直接去网上照抄即可。

这里要解释一下,我们的方差为什么使用其自然对数​​logvar​​。经过我的实验,如果让模型输出方差本身的话,就要在损失函数里对齐取一次自然对数。如果方差很小,趋于0的话,方差的对数就趋于无穷。这表现在loss里会出现nan。因此,在神经网络中我们应该避免拟合要取对数的数,而是直接去拟合其对数运算结果。

准备好了损失函数,剩下就是常规的训练操作了。

def train(device, dataloader, model):
    optimizer = torch.optim.Adam(model.parameters(), lr)
    dataset_len = len(dataloader.dataset)

    begin_time = time()
    # train
    for i in range(n_epochs):
        loss_sum = 0
        for x in dataloader:
            x = x.to(device)
            y_hat, mean, logvar = model(x)
            loss = loss_fn(x, y_hat, mean, logvar)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            loss_sum += loss
        loss_sum /= dataset_len
        training_time = time() - begin_time
        minute = int(training_time // 60)
        second = int(training_time % 60)
        print(f'epoch {i}: loss {loss_sum} {minute}:{second}')
        torch.save(model.state_dict(), 'dldemos/VAE/model.pth')

训练好模型后,想要查看模型重建数据集图片的效果也很简单,去dataloader里采样、跑模型、后处理结果即可。

def reconstruct(device, dataloader, model):
    model.eval()
    batch = next(iter(dataloader))
    x = batch[0:1, ...].to(device)
    output = model(x)[0]
    output = output[0].detach().cpu()
    input = batch[0].detach().cpu()
    combined = torch.cat((output, input), 1)
    img = ToPILImage()(combined)
    img.save('work_dirs/tmp.jpg')

想用模型随机生成图片的话,可以利用之前写好的模型采样函数。

def generate(device, model):
    model.eval()
    output = model.sample(device)
    output = output[0].detach().cpu()
    img = ToPILImage()(output)
    img.save('work_dirs/tmp.jpg')

在3090上跑这个实验,100个epoch需要5个多小时。但是,模型差不多在10多个epoch的时候就收敛了。最朴素的VAE的重建效果并不是很好,只能大概看出个脸型。这可能也和我的模型参数较少有关。

随机生成的图片也是形状还可以,但非常模糊。

#xxx

#xxx

#xxx

#xxx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值