块状掩码技术详解:视觉自监督预训练与PyTorch实现

发布时间:2026/10/21 4:37:28
块状掩码技术详解:视觉自监督预训练与PyTorch实现 做自监督预训练的人多半都有这种感觉NLP那边被验证得滚瓜烂熟的“掩码语言模型”套路搬到视觉上来总是有点水土不服。逐像素打码模型学到的全是低频纹理整行整列遮掉又经常训出鬼影。后来在视觉Transformer的框子里试了块状掩码Blockwise Mask情况才真正发生质变。这个技术说白了就是先把图像切成一块块的补丁Patch再把其中一部分补丁整体遮住让模型从剩下的可见补丁里推理被遮住的那部分应该长什么样。它解决的不是“认出图中有什么”而是“图里面不同区域之间怎么互相解释”。这篇文章写给两类人一类是正准备做自监督预训练、在对比学习和掩码重建之间摇摆的算法工程师另一类是对Vision Transformer感兴趣但被各种Mask策略绕晕的同学。我会从块状掩码为什么有效讲起拆解它的核心机制然后给出一套能直接跑的PyTorch实现思路最后把我自己踩过的坑、调过的参、总结过的问题排查清单全部摆出来。读完你应该能自己动手复现一个中等规模的块状掩码预训练实验也能判断什么时候该用块状掩码什么时候其实该用对比学习。1. 块状掩码到底在解决什么问题1.1 从NLP掩码到视觉掩码的迁移落差BERT那套做法在NLP里的成功让很多人自然想到给图像也随机抠掉一些像素然后让模型去预测被抠掉的部分是不是就能学到好特征理论上没问题但一上手实验就会发现差距。文本是离散符号序列一个词被遮住后语义上下文会给出非常明确的约束模型必须理解句子的语义才能预测准确。图像不一样相邻像素本来就高度相关你抠掉一个单点或者很小的区域模型稍微看看周围的颜色梯度就能把数值“插值”出来这个过程完全不需要理解图像的高级语义。这就像让一个学生做填空题题目是“天空是____色的”。如果横线很短、空格很小学生只要看到天空两字就能填“蓝”根本不需要懂气象、光学或者大气散射。而你如果想逼他真正理解云的形状、光的传播、时间与天气的关系就得把一整片句子甚至一段话都挖掉让他必须靠大范围推理才能补出来。块状掩码干的就是这件事它把遮挡区域放大到一块切断局部插值的捷径逼着模型去利用非局部的、全局的上下文信息。这个直觉后来在视觉Transformer的工作里被反复证实块的尺寸越大模型能学到的表征往往越偏向语义遮得越彻底重建任务对推理能力的要求就越高。所以块状掩码不是“掩码”的简单变体而是针对视觉数据特性设计的必要选择。1.2 块状掩码 vs 逐元素掩码任务难度的本质差异先明确两个概念。逐元素掩码Element-wise Masking指的是对每个像素或每个Token独立做二值化遮挡比如随机把图像中30%的像素置为灰色块状掩码Blockwise Masking则是把图像划分为规则或不规则的块一次性将整块区域的所有Token全部遮住。表面上看前者似乎更“细粒度”应该更适合重建任务。实际恰恰相反。逐元素掩码产生了一个隐藏的巨大问题被遮像素周围的可见像素构成了极强的局部线索模型很容易学到“用邻域加权平均来填空”这个保守策略。它确实能把训练损失降得很低但学到的特征不具备跨区域的语义关联能力。你可以把这种特征理解为“一个会写字的文员但不懂句子含义”。块状掩码把被遮区域从“像素点”变成一个“语义单元”。在视觉Transformer里一个Patch通常是16x16像素一块连续掩码往往覆盖4到16个Patch也就是64到256像素见方的领域。模型无法从紧邻的可见Token里直接插值必须跨越较远的空间距离寻找结构线索。比如遮住一块天空模型得通过远处的楼房轮廓、地面反射、云层位置来推断这块天空的颜色和层次。这种任务难度设计是自监督学习的核心任务的难度直接决定了特征的质量。太简单的任务学到的是捷径太困难的任务又训不收敛块状掩码做的就是在这中间找到那个“逼而不死”的平衡点。1.3 在自监督家族里找位置生成式任务和判别式任务的分野自监督学习粗略可以分成两个阵营。判别式方法以对比学习为代表核心思路是把同一张图的不同增强视图拉近、把不同图的视图推远模型学到的是“哪些特征能区分样本”生成式方法以掩码重建为代表核心思路是让模型从部分观测中恢复完整信号模型学到的是“各个观测成分之间如何互相解释”。块状掩码属于生成式阵营但它的目标函数又不是简单地预测像素。在实现层面它往往配合一个不对称的编码器—解码器结构编码器只处理可见的Patch轻量解码器负责从完整的位置序列里重建被遮Patch。这种生成任务和对比学习有本质差异对比学习天然更擅长学习全局不变性比如旋转不变、颜色不变块状掩码则更擅长学习结构先验和空间关系比如“一辆车通常停在地面上而不是浮在空中”。在实际项目里我个人的经验是如果下游任务是分类、检索这类需要全局判别的场景对比学习的性价比通常更高如果下游任务是检测、分割、修复这类对空间结构敏感的场景块状掩码预训练的特征往往更好用。两者不是替代关系更像两把不同形状的钥匙。2. 块状掩码的机制拆解几个必须弄懂的环节2.1 图像分块Patchify像素怎么变成Token要理解块状掩码先得理解视觉Transformer是怎么处理图像的。以常用的设定为例一张224x224的图片会被分成14x14个Patch每个Patch的尺寸是16x16像素。每个Patch再通过一个线性投影映射成TokenToken的维度通常取768或1024。做完这一步图像就从一个三维张量变成了一个二维序列形状是196x768196是序列长度768是每个Token的特征维度。这个分块过程看似简单但有几个细节直接影响块状掩码的效果。第一个是Patch大小。Patch越小序列越长模型能保留的空间细节越多但计算量也成倍增加。Patch越大序列越短长距离推理更容易但高频细节容易丢。块状掩码实验里16x16的Patch是各方面都相对均衡的选择。想验证这一点可以对比8x8和32x32两种设定8x8时掩码块稍大一点重建任务会非常简单模型可能偷懒32x32时Patch太大细节损失严重重建出来的图像总是糊成一团。第二个是重叠分块。一些实现会让相邻Patch之间有部分重叠相当于在用卷积神经网络处理图像时加了滑动窗口。重叠分块能平滑边界但对掩码任务未必有好处因为它破坏了Patch独立性让被遮区域更容易通过相邻Token的边界信息泄漏内容。我试过带重叠的Patch Embedding预训练Loss降得更快但下游微调效果没有明显提升有时反而更差。第三个是Patch级别的归一化。这里特别值得注意如果你用标准Vision Transformer的做法在每个Token送入网络之前做LayerNorm那是常规操作但如果你在分块后、掩码前对每个Patch的像素做归一化等于提前把对比度信息抹掉了这对重建任务非常不利。正确做法是把像素归一化放在模型输出端作为重建目标的一部分。2.2 掩码怎么生成随机块、连续块、语义块块状掩码的生成策略是个很容易被低估的细节。很多人一开始想的是“把14x14个Patch随机挑一部分遮掉”但这其实是逐Token掩码不是真正的块状掩码因为被遮Token在空间上大概率不连续局部插值的捷径依然存在。真正意义上的块状掩码应该保证被遮区域在空间上是成片的。实现方式主要有三种。第一种是低分辨率掩码上采样法。这是最常用也最推荐的做法。小尺寸图像例如28x28的随机二进制掩码可以先用双线性插值放大到196维Patch级别再通过阈值转成0/1。这么做的好处是可以精确控制块的大小和连续性阈值取0.5时掩码大致保持低分辨率下的布图结构连续性和随机性都有保障。第二种是随机矩形框法。直接在一个14x14的网格上随机选择起点和长宽画出一个矩形区域作为掩码块。你可以画一个也可以循环画几个直到达到目标掩码比例。这种方法比较直观但缺点是掩码比例不容易精确控制需要多试几次校准而且矩形框的形状固定容易造成空间位置偏置。第三种是语义感知掩码。通过边缘检测、显著性检测或其他启发式手段找出图中语义比较完整的区域然后优先掩码这些区域。这种方法理论上最能逼出高级语义推理但实现成本高而且对上游的检测质量有依赖。我见过一个复现项目用它做过医学图像实验效果确实比随机块好但工程复杂度高了不少普通任务没必要上。从稳定性角度我个人强烈建议从第一种低分辨率上采样法开始先跑通流程再考虑要不要升级到更复杂的策略。后面第三部分我会给出可直接用的代码。2.3 重建什么像素级目标与特征级目标被掩掉的Patch重建目标也有讲究。最朴素的目标是被掩Patch的原始像素值。实现方式可以是对解码器输出的Token做一次线性映射把输出的特征维度映射到Patch像素数16x16x3768然后和被掩区域的真实像素做均方误差。这里有一个著名的坑直接对原始像素做MSE模型倾向于优先学习低频成分因为低频成分误差梯度更大、收敛更快结果就是重建出的图像“大形不差细节全无”。一个简单有效的改进是对每个被掩Patch的像素做归一化计算每个Patch像素的均值和方差把真实像素和目标像素都减均值、除方差后再计算损失。归一化后的目标强迫模型去预测Patch内部的纹理结构而不是只预测一个平均色块。我在一个复现实验里对比过这两者的差异不归一化时重建图看起来像打了马赛克的模糊块归一化之后重建图的边缘和纹理明显更锐利虽然像素误差反而大了但下游检测任务的mAP高了好几个点。另一种做法是特征级重建。不用像素做目标而是用某个预训练网络的中间特征做目标模型只需要预测特征空间中Patch对应的向量。特征级重建在语义层面更“柔软”不容易被高频噪声带偏但会引入额外网络训练和存储成本都上升。对小规模实验来说归一化像素损失已经足够就算以后要上特征级重建也建议先跑通像素级版本。2.4 掩码比例为什么是关键超参数掩码比例是指被遮住Patch数占总Patch数的比例。这个参数在块状掩码里的重要性怎么强调都不过分。它直接决定了自监督任务的难度曲线。定量来看假设Patch总数为196即14x14网格掩码比例为50%时可见Token有98个掩码比例为75%时可见Token只剩49个掩码比例到90%时可见Token只有约20个。可见Token越少每个Token蕴含的信息量必须越大模型被迫做更抽象的高层理解。按照我自己的实验经验在ImageNet规模的数据上块状掩码比例在60%到80%之间通常能取得较好的平衡。低于40%时重建任务太简单模型学到的特征和自编码器拉不开差距高于85%时任务难到训练不稳定Loss曲线上蹿下跳容易陷入局部最优需要额外技巧才能训好。但掩码比例不是孤立存在的它和块的大小紧密相关。块小、排列密集时即使掩码比例不变任务仍然偏向局部块大、排列稀疏时任务自动变成全局推理。后面调参部分我会给出一组消融数据方便你理解这两者怎么配合。3. 实操手写一个块状掩码预训练Demo3.1 数据准备与分块实现我不会给你搬一套庞大的训练框架而是聚焦块状掩码最核心的几个代码片段你自己拼到现有训练流水线里就能用。假设你已经有了一批图片大小统一为224x224。第一步是把图片变成Patch序列。这里直接手工实现不依赖额外库方便你理解每一步发生了什么。import torch import torch.nn.functional as F def patchify(images, patch_size16): images: (B, 3, H, W) return: (B, N, P*P*3)N (H/P)*(W/P) B, C, H, W images.shape num_h H // patch_size num_w W // patch_size # 切块后重排成 token images images.reshape(B, C, num_h, patch_size, num_w, patch_size) images images.permute(0, 2, 4, 3, 5, 1) # B, num_h, num_w, patch_h, patch_w, C images images.reshape(B, num_h * num_w, patch_size * patch_size * 3) return images def unpatchify(tokens, patch_size16, channels3): tokens: (B, N, P*P*C) return: (B, C, H, W) B, N, _ tokens.shape num int(N ** 0.5) H W num * patch_size tokens tokens.reshape(B, num, num, patch_size, patch_size, channels) tokens tokens.permute(0, 5, 1, 3, 2, 4).contiguous() return tokens.reshape(B, channels, H, W)这段代码里有个容易错的地方不要用view直接变形因为permute会改变内存布局后面必须跟着contiguous()否则训练时会随机报错或者结果飘忽不定。我排查过几次类似问题最后都发现是内存布局没处理好。实际项目里你可能会用einops库简化操作写法是Rearrange(b c (h p1) (w p2) - b (h w) (p1 p2 c))。两种方式等价但手写版本更利于调试。3.2 随机块状掩码生成代码这里给出一段经过验证的低分辨率掩码上采样实现。核心思路是先在28x28的低分辨率空间生成随机掩码然后上采样到14x14的Patch粒度。def blockwise_mask(images, mask_ratio0.75, mask_block_size8): 生成块状掩码的 mask 张量。 images: (B, 3, H, W)仅用于获取尺寸 return: mask: (B, N)1 表示需要重建(被遮住)0 表示可见 其中 N (H/P)*(W/P) B, C, H, W images.shape patch_size 16 num_patches (H // patch_size) * (W // patch_size) # 196 # 低分辨率网格尺寸保证 mask 的局部连续 grid_h, grid_w H // mask_block_size, W // mask_block_size low_mask torch.rand(B, 1, grid_h, grid_w) # B,1,gh,gw # 插值到 patch 粒度 high_mask F.interpolate( low_mask, size(H // patch_size, W // patch_size), modebilinear, align_cornersFalse ).reshape(B, num_patches) # 从高分辨率概率图中得到二值掩码 # 先用排序法得到阈值保证整体掩码比例接近 mask_ratio k int(num_patches * mask_ratio) threshold high_mask.topk(k, dim1).values[:, -1:] mask (high_mask threshold).float() return mask这里用了一个小技巧不是直接对随机概率图设0.5阈值而是把概率值排序后取第k大值作为动态阈值。这样不管上采样后的概率分布长什么样最终掩码比例总能精确地落在mask_ratio附近不用反复调阈值训练不同数据集时也稳当。块大小mask_block_size控制的是低分辨率网格的分辨率。比如设成8意味着最低分辨率是8像素一块上采样到Patch粒度后掩码块的平整区域会比较大。想生成更细碎的块状掩码把mask_block_size从8改成4即可想生成超大连续块改成16甚至32。3.3 不对称编解码器结构怎么搭块状掩码通常配合不对称编解码器。编码器往往是标准的Vision Transformer但只处理可见的Token解码器则是一个轻量Transformer负责从编码结果重建全图。为什么不对称因为掩码比例高的话比如75%的Patch被遮住可见Token只有49个编码器的计算量只是全图的四分之一。轻量解码器虽然要处理全部196个Token但它只需要几层就够整体计算开销反而比“编码器直接处理全图”低很多。这个设计是块状掩码在大模型上能落地的核心原因。搭建的时候有几个关键点编码器输入部分要先把可见Token抽取出来。假设经过Patch Embedding后每个Token是768维同时我们有一份完整的位置编码形状是196x768。我们需要从完整位置编码里抽出对应可见位置的那些行和可见Token相加后再送入编码器。这里最常见的错误是“全图Token都过编码器只是Loss只算被遮部分”。这样做在数学上没错但完全违背了块状掩码的设计初衷编码器看到了所有Patch就没有“从部分信息推理整体”的压力了。预训练效果会显著变差。我见过好几个复现项目Bug藏得很深就是从这一步开始走歪的。解码器部分被遮位置需要填充一个可学习的Mask Token。你可以把Mask Token理解成一个“空占位符”它告诉解码器“这里是缺失的请重建”。注意可学习的Mask Token和输入图片完全无关只和位置编码绑定。把所有可见Token向量和Mask Token拼接回完整顺序后再运行解码器。下面给一个简化版结构代码帮助你把握整体轮廓class BlockMaskAutoEncoder(nn.Module): def __init__(self, embed_dim768, decoder_dim512, num_encoder_layers12, num_decoder_layers8, patch_size16, img_size224): super().__init__() self.patch_embed nn.Linear(patch_size * patch_size * 3, embed_dim) self.pos_embed nn.Parameter(torch.randn(1, (img_size // patch_size) ** 2, embed_dim)) # 编码器只处理可见 token encoder_layer nn.TransformerEncoderLayer(d_modelembed_dim, nhead12, batch_firstTrue) self.encoder nn.TransformerEncoder(encoder_layer, num_layersnum_encoder_layers) # 解码器处理全序列 self.decoder_embed nn.Linear(embed_dim, decoder_dim) self.mask_token nn.Parameter(torch.randn(1, 1, decoder_dim)) self.decoder_pos_embed nn.Parameter(torch.randn(1, (img_size // patch_size) ** 2, decoder_dim)) decoder_layer nn.TransformerEncoderLayer(d_modeldecoder_dim, nhead8, batch_firstTrue) self.decoder nn.TransformerEncoder(decoder_layer, num_layersnum_decoder_layers) # 重建输出头 self.pred_head nn.Linear(decoder_dim, patch_size * patch_size * 3) def forward(self, x, mask): B, N, _ x.shape # x 是 patch 化后的 token # 抽取可见位置 visible_ids torch.nonzero(mask 0, as_tupleFalse) # 粗糙做法仅供演示 # 实际建议通过排序、拼接等方式一次性完成 Batch 内抽取 x_visible x[mask 0].reshape(B, -1, x.shape[-1]) pos_visible self.pos_embed[mask 0].reshape(B, -1, x.shape[-1]) x self.encoder(x_visible pos_visible) # 补齐完整序列 x self.decoder_embed(x) full self.mask_token.repeat(B, N, 1) full[mask 0] x full full self.decoder_pos_embed full self.decoder(full) # 只对被掩位置计算输出 pred self.pred_head(full) return pred, mask这段代码里visible_ids等号右边是粗糙索引实际工程要用高级索引批量处理我故意写成这个形式是提醒你“每一步都要想清楚形状”。你也可以用torch.where加gather来做或者干脆把Patch序列分成可见和不可见两组用索引数组把编码结果放回原位置。3.4 训练流程与关键细节训练逻辑其实很常规输入图片生成MaskPatchify进模型算被掩位置的重建损失反向传播。我讲三个容易被忽略的细节。第一归一化像素损失。如前面所说直接MSE容易让模型偷懒去预测平均色块。标准做法是提前统计每个Patch的均值方差然后在损失计算时对被掩Patch做标准化def normalized_mse_loss(pred, target, mask): # pred/target: (B, N, P*P*3)mask: (B, N) B, N, _ pred.shape # 对每个 patch 做标准化 pred_mean pred.mean(dim-1, keepdimTrue) pred_std pred.std(dim-1, keepdimTrue) 1e-6 target_mean target.mean(dim-1, keepdimTrue) target_std target.std(dim-1, keepdimTrue) 1e-6 norm_pred (pred - pred_mean) / pred_std norm_target (target - target_mean) / target_std loss (norm_pred - norm_target) ** 2 loss loss.sum(dim-1) * mask # 只保留被掩位置 return loss.sum() / mask.sum()这里的1e-6是防止空白Patch出现零方差不加这个小常数偶尔会训出NaN。经验告诉我遇到Loss突然变NaN先检查是不是标准差为零导致的。第二位置编码的更新策略。编码器里可见Token使用的位置编码来自完整位置编码的子集所以在预训练时位置编码必须参与梯度更新不能像某些Transformer分类任务里那样冻结。一开始我犯过这个错误位置编码完全不更新结果模型完全无法感知相对位置结构重建出来的图像像是把碎片硬拼起来。第三学习率调度。块状掩码预训练的任务分布和分类任务不同模型先在“部分信息补全”的问题里探索后期才渐渐收敛。常见配置是用AdamW优化器基础学习率1e-4到3e-4之间配合余弦退火调度。Batch Size在256到1024之间都可以但小Batch Size下要相应调低学习率否则震荡很严重。4. 调参心得与避坑清单4.1 掩码比例与块大小怎么配合掩码比例和块大小这对组合是我做实验时最先发现“非线性关系”的一组参数。单独调掩码比例很多问题会被块大小掩盖单独调块大小又会和掩码比例互相打架。把它们拆开看才有清晰结论。我做过的对照组大致如下你可以把它当作初始参考掩码比例块大小(低分辨率网格)观察现象建议40%4任务偏简单重建图清晰特征泛化一般不推荐入门60%4训练稳定重建与下游表现均衡小数据集推荐75%8训练略慢重建图较抽象下游表现好中等规模以上推荐85%8训练波动大重建开始不稳定需要额外技巧支持90%16收敛困难Loss偶发震荡仅限大规模试验从表格可以看出一条规律掩码比例越高块也应该越大。原因在于高掩码比例下可见Token已经很少如果块还很小这些星星点点的小块位置随机分布上下文被切得太碎模型很难找到连贯的结构线索块大一点可见区域才能保持一定的连续性给重建提供足够的锚点。反过来低掩码比例下块太大也不行。你想只有40%的Patch被遮把这些Patch聚成一个巨大的连续区域可见区域就集中在图片的另一侧模型很容易学到“左边看着办右边照着抄”这样的位置捷径。4.2 学习率、Batch Size与训练轮次的影响块状掩码预训练比同等规模的分类训练要敏感得多。分类任务你用1e-3学习率可能跑得挺好换到掩码重建任务上同样的模型可能会在几百步之内Loss直接炸到NaN。我按照多个项目的调参记录总结出几条可复用的经验学习率方面ViT-Base规模的编码器建议初始学习率取1e-4到2e-4Batch Size为256时取1e-4起步比较稳妥如果你把Batch Size提高到1024学习率可以相应拉到3e-4附近。这里有个简单的经验法则学习率大致和Batch Size的平方根成正比。但别生搬硬套块状掩码的重建任务本质是回归问题对损失曲线上细小波动更敏感宁可保守也别贪快。Batch Size对对比学习和掩码重建的影响方式完全不同。对比学习里大Batch意味着同一批次里有更多负样本效果直接受益掩码重建的Loss里没有负样本概念大Batch只是让梯度估计更稳定但提升不如对比学习那么明显。所以如果你资源有限不用为了这块状掩码实验硬凑1024的Batch Size512已经足够。训练轮次上常见做法是预训练300到800轮。块状掩码的收敛速度其实比对比学习快因为每个样本都被切成多份信息利用率高但它的“稳定到高性能”阶段很靠后需要耐心。我见过一个模拟实验150轮时下游任务表现平平到400轮突然涨了一截早期容易误以为模型没学到东西而提前终止。4.3 常见踩坑问题速查表整理了几个我在复现块状掩码时真正踩过的坑以及排查思路。Loss正常下降但下游效果差优先怀疑任务过于简单。检查你是不是用了带有重叠的Patch Embedding或者掩码块太小、掩码比例偏低。一个判断方法是看重建图如果重建图清晰但丢失语义结构比如把矩形物体重建得边缘模糊大概率是任务捷径被模型学到了。重建图像整体发灰说明模型偏向预测平均像素。用上面提到的归一化像素损失就能大幅缓解。如果你已经用了归一化还是发灰看解码器层数是不是太浅。解码器层数低于4的话信息从可见Token传播到被掩Token的路径太短细节容易丢。把掩码比例精确设置成期望值有一个隐藏陷阱排序阈值法在高掩码率下会把很多相近概率的Patch挤在一起导致掩码在空间上不平均。比如你mask_ratio设75%一半时间掩码区域集中在一侧另一半时间集中在另一侧这个非随机性会干扰训练。解决办法是在低分辨率概率图上先打个小的随机噪声再上采样让空间分布更均匀。训练到一半开始震荡大概率是学习率太高或者位置编码和小Batch Size的配合出了问题。你先降学习率试试别看Loss已经降下来了就掉以轻心。如果降学习率没用检查有没有对位置编码做梯度裁剪或者干脆给位置编码单独设小学习率。4.4 一组快速消融思路真要在自己的数据集上跑块状掩码实验不建议上来就做全量消融。我推荐按下面这个顺序快速验证先固定块大小比如8只扫掩码比例四个值50%、60%、75%、85%。每个配置跑50到100轮看预训练Loss的曲线和收敛后的重建质量。然后固定一个最优掩码比例扫块大小四个值4、8、16、32。同样的轮次对比下游任务效果。最后一轮固定最优组合把Batch Size从256提到512学习率相应微调看是否有增益。这样三轮消融总共也就12个实验不算多但能帮你把块状掩码的“任务难度甜点区”摸清楚。我自己的经验是在大部分中规模数据集上掩码比例75%加块大小8是比较稳妥的起点。5. 块状掩码的进阶用法与扩展方向5.1 预训练后的微调策略块状掩码预训练出来的编码器要怎么应用到下游任务这和普通Vision Transformer的微调流程大体一致但有三个值得注意的点。第一微调时要不要保留解码器。通常不需要。解码器是为重建任务服务的它在预训练中逼着编码器把信息组织成可重建的形式但下游分类或检测不需要这个能力。微调阶段直接把编码器接上任务头比如分类头冻结解码器或直接丢掉即可。从工程角度丢掉解码器还能省不少显存。第二位置编码要不要插值。如果你的下游图片分辨率不是224x224而是512x512Patch数会变成32x321024和预训练时的196不一样。这时要对位置编码做双线性插值把它从14x14网格映射到32x32网格。插值时别只做一次先在14x14的位置上用双线性插值得到16x16再进一步插值到32x32两步走的效果比单步放大更好这是我从几次实验中总结的经验。第三微调时的初始化。批归一化统计量如果有要重新计算LayerNorm不受影响。AdamW的动量信息应该清空否则预训练阶段积累的梯度动量会干扰新任务头的收敛。我见过有同学直接在预训练状态上继续微调结果任务头训得很好但主干的低级特征一直调不过来整体性能反而比从头训练差。5.2 跨模态与视频场景的块状掩码块状掩码的思想很容易扩展到其他信号上。视频场景里每一帧都可以分块然后按时间维度做联合掩码。掩码的设计变成三维的空间维度的块、时间维度的片、以及空间—时间联合块。视频比静态图像更适合高掩码比例因为相邻帧之间的时间冗余很高模型如果不理解运动规律很难从可见帧预测被遮帧里的内容。跨模态方向典型做法是把图像Patch和文本Token放到同一个序列里联合掩码。图像里的一个块被遮住文本里也可能遮住一个片段模型要同时利用图像和文本两种模态的信息去补全。块状掩码在这里的作用很有意思它打破了模态间的信息壁垒因为图像被遮后文本成了唯一的信息来源模型被迫学会“视觉概念在语言里怎么描述”这种跨模态映射。不过这类实验难度大对数据质量要求也高。如果你刚接触块状掩码还是先把单一图像模态玩明白再说盲目上跨模态容易在数据预处理上浪费大量时间。5.3 不训练直接用修复、插值与异常定位最后说一个容易被忽略的应用块状掩码预训练好的模型本身就是一个天然图像修复器。虽然预训练编码器的输出主要服务于自监督表征但如果你保留解码器输入一张有缺失区域的图片指定掩码位置后模型可以直接输出重建结果。我在一个数据修复项目里这么干过监控摄像头画面出现了坏道数据损坏区域比较大直接把我预训练好的掩码模型拿出来把损坏区域作为掩码输入轻量解码器输出的填充结果加上一个小修补损失的后期处理效果比传统插值好不少。它不需要专门训练修复模型属于典型的前期投入后期零成本复用。异常检测也很有意思。正常图片经过重建后重建误差应该很小如果图片里出现了模型没见过的异常比如产品缺陷、拍摄设备故障、医学影像里的异常阴影这些区域的块状重建误差会显著偏高。把误差图可视化就能直接得到异常位置的定位结果。这种思路在工业质检场景里很实用因为它完全不需要标注异常样本。“不训练直接用”听起来很省事但它对预训练数据分布有要求预训练数据要尽量贴近你实际要处理的图片风格。拿纯自然风景图预训练直接拿去修工业零件图效果必然打折扣。有条件的话预训练数据里掺杂一部分目标领域的数据哪怕只有20%对下游修复质量的提升都很明显。我自己在几个项目里的体感是块状掩码是少数几种“能自己讲故事”的自监督方案它的预训练过程就能直观看到学到的结构理解能力。如果你碰到过对比学习训练像黑盒、不知道模型到底学到了什么的问题不妨试试块状掩码。它的调参维度多但每一步都有明确的现象解释验证起来并不难。最后分享一个推荐的操作顺序先用小数据集、小模型把第3部分的代码跑通观察重建图和Loss曲线再逐步放大数据和模型规模稳定之后再去调掩码比例和块大小。别一上来就追逐大模型高掩码率先把“任务难度”和“模型容量”匹配起来后面所有实验都会顺手很多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询