做域泛化Domain Generalization, DG研究的人大概率都经历过这种绝望论文里报告的精度高得吓人代码开源得也很痛快但你拿过来一跑结果差出一大截。一开始你会怀疑自己的环境有问题反复排查之后才意识到问题出在评测方式上——数据划分不同、预训练模型不同、超参数搜索范围不同甚至有人偷偷用目标域调参。这样的研究生态导致大量 DG 论文的结论根本无法复现所谓新算法优于旧算法很多只是一场自说自话。DomainBed 就是冲着这个乱象来的。它把数据集、训练协议、超参数选择策略、算法接口全部标准化让所有 DG 算法在同一套规则下公平比拼。想入坑域泛化、分布外泛化或者鲁棒机器学习的研究人员和工程师这套工具几乎是绕不开的起点。我最早接触 DomainBed 是在复现某篇 DG 论文失败之后当时花了两周时间把作者的代码从数据加载到训练循环全部读了一遍最后发现他在 PACS 上做超参数选择时用了一部分目标域数据做验证。这个操作直接让结果虚高了十几个点。后来我把实验迁移到 DomainBed 上重新跑发现所谓碾压 ERM的新算法其实和 ERM经验风险最小化打平手。这个经历让我彻底明白不是算法不够聪明而是评测太混乱。所以这篇文章我想从 DomainBed 的设计思路讲起把 ColoredMNIST、RotatedMNIST、PACS、OfficeHome、DomainNet 这些常用基准数据集逐个拆开再结合我自己跑实验时踩过的坑给出一份能直接上手的实操指南。1. 为什么域泛化研究需要一个标准考场——DomainBed 的诞生背景1.1 复现不了的算法与各说各话的榜单2020 年前后的域泛化论文几乎每篇都要在 PACS、VLCS、OfficeHome 上刷一遍精度。表面看大家都在用同样的数据集实际上每个课题组的处理方式千差万别。最典型的问题有三个。第一数据划分方式不统一。有的论文按类别比例随机划分训练测试集有的按图像风格划分域还有的把同一个域里的图像随机打散塞进训练集和测试集——后者其实已经不属于域泛化而是在做普通图像分类。第二超参数选择策略混乱。有些方法在目标域上做 early stopping有些用目标域做模型选择这相当于开卷考试分数自然高。第三数据增强、预训练权重、优化器配置各写各的没有统一基线导致所谓的提升可能只是来自更大的 batch size 或者更长的训练步数。当时整个领域缺少一个像 ImageNet 之于图像分类那样的公共基准。ImageNet 的成功经验告诉我们一个统一的评测平台能极大加速领域发展因为它省去了每个人重复造评测流程的功夫让大家把精力集中在算法本身。DomainBed 想做并且做到了的正是这件事。1.2 DomainBed 到底做了什么统一协议、统一划分、统一接口DomainBed 是一个基于 PyTorch 的开源框架核心目标是把域泛化实验的每一个环节都标准化。它把数据集封装成统一的 PyTorch Dataset 类每个数据集都按照域来划分训练集和测试集而不是随机划分。所有算法都实现成统一的 Algorithm 接口训练脚本、评测脚本、日志记录、结果聚合全部复用同一套代码。从架构上看DomainBed 的核心组件包括几个部分数据加载层负责下载或生成数据集并缓存到本地算法层定义了 ERM、IRM、DRO、CORAL、MMD、Mixup 等一批常用的 DG 算法训练脚本负责跑一个完整的训练-验证-测试流程还有一套结果记录模块把每个 trial 的精度、损失、超参数配置写入 CSV 文件。用户只需要通过命令行参数指定 --dataset、--algorithm、--test_env剩下的流程全部由框架完成。这套设计最大的好处是当你要比较两个算法时它们面对的是完全相同的数据划分、完全相同的预训练权重、完全相同的超参数搜索空间。任何性能差异只能来自算法本身。它背后的方法论论文 ICLR 2021 那篇In Search of Lost Domain Generalization我建议所有做 DG 的人都读一遍里面有很多反直觉的发现。1.3 一个容易被忽视的结论ERM 并不弱DomainBed 跑出来的第一个重磅结论就是很多先进算法其实打不过 ERM。论文在七个数据集上系统比较了十几个算法发现当超参数选择策略严格限定为 training-domain validation set 时ERM 的平均排名非常高甚至在部分数据集上超过了绝大多数专门设计的 DG 算法。这个结论当时在圈子里引起了很大震动。注意这并不意味着域泛化研究没有意义。它真正说明的是先前大量论文报告的显著提升很大一部分来自不公平的比较。比如某个算法用了目标域做模型选择自然会比用源域验证集选择模型的 ERM 高很多。ERM 本身具备不错的跨域鲁棒性尤其在使用 ImageNet 预训练模型时很多 DG 算法的增益会被预训练特征本身抹平。DomainBed 把这个事实摆到台面上之后DG 领域的论文质量整体提升了一个档次后来大家再做对比实验基本都在 DomainBed 协议下进行。2. 七个基准数据集逐个拆解从合成玩具到真实场景2.1 ColoredMNIST 与 RotatedMNIST可控的虚假相关性实验室DomainBed 一共有七个基准数据集按照从合成到真实的难度可以排成一个序列。最轻量的是 ColoredMNIST 和 RotatedMNIST它们都由 MNIST 改造而来单张图像只有 28x28 像素训练速度极快特别适合快速验证想法。ColoredMNIST 的构造非常有讲究。它把 MNIST 里的数字按大小分成两组——数字 0 到 4 作为一类5 到 9 作为另一类构造一个二分类任务。然后给图像着色让颜色和标签之间形成虚假相关性在训练环境里80% 或 90% 的红图对应第一类蓝图对应第二类测试环境里这个相关性完全反转只有 10% 的红图对应第一类。也就是说如果模型学会了红色就选第一类这条捷径训练精度会很高但测试时会被彻底打爆。真正跨域不变的信号只有一个数字本身的形状。RotatedMNIST 的思路更直观一些。它把 MNIST 图像旋转不同角度每个角度构成一个域比如 0 度、15 度、30 度、45 度、60 度、75 度。模型在部分角度上训练在未参与训练的角度上测试考验的是对旋转这种几何变化的不变表征能力。这两个数据集都属于可控实验室因为它们能精确创造某种分布偏移方便研究者在受控条件下验证算法是否真的学到了不变特征还是仅仅在拟合表面相关性。2.2 VLCS、PACS、OfficeHome跨风格泛化再往上一层是三个真实图像数据集VLCS、PACS、OfficeHome。VLCS 来自四个著名的视觉数据集拼接分别是 VOC2007、LabelMe、Caltech101 和 SUN09总共包含 5 个类别。它把数据集来源当作域同一类物体在不同数据集中呈现完全不同的拍摄风格、背景和构图。VOC2007 和 Caltech101 里的椅子长得差别很大模型需要忽略这些风格差异抓住真正的类别特征。PACS 是风格差异最大的一个数据集只有 4 个域Photo照片、Art Painting艺术画、Cartoon卡通、Sketch线稿7 个类别。艺术画里的马可能带着浓厚的油画纹理卡通里的马造型夸张线稿里的马只剩几条轮廓线。从 Photo 训练出来的模型直接测试到 Sketch 上难度非常高所以 PACS 是 DG 论文里最常出现的数据集之一很多算法的改进在 PACS 上都容易看到明显差异。OfficeHome 则更贴近实际办公场景有 4 个域——Art、Clipart、Product、RealWorld共 65 类图像量约 1.5 万张。它的类别划分非常细包括各种办公用品、电子设备对细粒度识别能力要求更高。相比 PACS 的简单分类OfficeHome 更考察模型在特征层次上的泛化这往往比粗粒度分类更能反映算法的真实水平。2.3 TerraIncognita 与 DomainNet真实场景的残酷考验如果前面的数据集还带着一定的实验室感TerraIncognita 和 DomainNet 就是彻头彻尾的真实世界考验。TerraIncognita 来自野外相机陷阱拍摄的动物图像按拍摄地点划分成 4 个域共 10 类动物。这套数据集的难点在于环境千差万别有的是热带丛林有的是草原光照、遮挡、动物姿态都极不规律而且类别在实际图像中的比例很不均衡。模型很容易偷懒通过学习背景植被颜色来分类而不是真正识别动物本身。DomainNet 是 DomainBed 里规模最大、难度最高的数据集也是我花时间最多的地方。它包含 6 个域clipart、infograph、painting、quickdraw、real、sketch共 345 类图像总量大约 59 万张。这里的类别人数巨大很多类别在不同域中的视觉表现差别已经不只是风格而是语义鸿沟。最典型的是 quickdraw它是用户用简笔画工具画的涂鸦跟真实照片在纹理、色彩、形状上都几乎没有任何共同特征。infograph 也很有挑战性它是包含文字和复杂图形的信息图识别难度远高于普通照片。因为类别太多、图像质量参差很多论文会选择使用其中 126 个类别子集也就是常说的 DomainNet126来降低训练开销和类别不平衡带来的干扰。2.4 各数据集规模与难度的横向对比我整理了一个表方便大家根据实验需求选择合适的数据集。数据集域数类别数图像量输入尺寸主要难点ColoredMNIST32约7万28x28x3颜色与标签的虚假相关性反转RotatedMNIST610约7万28x28x1几何旋转变化VLCS45约1万224x224数据来源风格差异PACS47约1万224x224风格跨度极大OfficeHome465约1.5万224x224细粒度类别多TerraIncognita410约2.4万224x224真实野外场景、背景混淆DomainNet6345约59万224x224类别极多、quickdraw 与真实域差异巨大选择数据集的策略其实很直接。起步阶段或者调试算法时用 ColoredMNIST几分钟就能跑完一个 trial方便快速定位问题。验证算法有没有基本效果用 PACS因为风格差异大、训练样本少算法差距很容易体现。准备写论文、做最终对比实验时必须上 DomainNet否则审稿人会质疑数据集多样性不够。3. 评测协议才是灵魂训练/验证/测试怎么划分超参数怎么选3.1 留一域评估每轮留一个域做目标DomainBed 评估协议的核心是留一域评估leave-one-domain-out。对每个数据集依次把其中一个域作为测试目标域其余所有域作为源域用于训练。这意味着每个数据集要跑 N 轮实验N 等于域的个数。比如 PACS 有 4 个域就要分别以 Photo、Art Painting、Cartoon、Sketch 作为 test_env 跑四轮最终报告四个目标域精度的平均值。这个协议和普通机器学习里的 train/test 随机划分有本质区别。普通划分把同一域内的图像随机分到训练集和测试集两边数据分布高度一致测出来的是内分布精度。而留一域评估要求测试域完全不参与训练测的是外分布精度这才是真正的域泛化能力。刚开始用 DomainBed 时容易犯的错就是自己额外做数据划分甚至把测试域样本混进训练集导致结果虚高。直接用框架自带的划分脚本就对了。3.2 超参数选择的纪律绝不看目标域评测协议里最关键、也最容易被忽略的环节是超参数选择策略。DomainBed 给出了三种策略其中两种合规一种违规。第一种是 training-domain validation set把源域的一部分样本单独抽出来做验证集在验证集上选择超参数然后用选定的超参数在训练集上重新训练再在目标域上测试。第二种是 leave-one-domain-out cross-validation在源域里再留一个域做验证集轮流选超参数。第三种是 test-domain validation set直接用目标域做验证集这是严重的数据泄露DomainBed 明确禁止。为什么超参数选择策略这么重要因为很多 DG 算法对超参数极度敏感同一算法在好参数下能比坏参数高十多个点。如果允许用目标域调参任何方法都能刷出漂亮的分数。DomainBed 把这一点固定下来之后在源域验证集上选超参数就成了标准动作。我自己做过对比某些算法在目标域调参时比 ERM 高 10 个百分点换到源域验证集调参后反而比 ERM 低差距就是这么来的。3.3 训练细节从优化器到数据增强的公平基线DomainBed 对训练细节也有严格约定目的是让所有算法处于同一基准线上。默认 backbone 是 ResNet-50使用 ImageNet 预训练权重输入图像统一缩放到 224x224做随机裁剪和水平翻转增广优化器可以选 SGD 或 Adamlearning rate 通过网格搜索确定训练步数根据数据集规模设定避免小数据集过拟合、大数据集欠拟合。有一个细节需要特别强调DomainBed 里每个算法都有自己预设的超参数搜索空间但这些空间是在框架开发时统一敲定的而不是某个算法专门调优过的。这保证了所有方法在搜索空间大小和搜索预算上基本公平。跑实验时也不要自己随意放大搜索空间否则会让该算法在对比中占便宜审稿人一眼就能看出来。另一个值得注意的点是随机种子。DomainBed 默认每个配置跑多个随机种子取平均因为 DG 实验的方差通常比较大尤其在小数据集上单次试验结果可能波动好几个点。至少跑三个种子再报告均值如果资源允许跑五个更好。4. 动手实操把 DomainBed 跑起来4.1 环境准备与安装DomainBed 的安装非常直接前提是你有一个能跑 PyTorch 的环境。建议 Python 3.8 以上PyTorch 1.7 以上torchvision 需要匹配 PyTorch 版本。GPU 建议显存至少 8GB因为 ResNet-50 在 224x224 输入下batch size 64 时的显存占用大约 6-8GB如果跑 DomainNet 这种大数据集16GB 显存会更从容。git clone https://github.com/facebookresearch/DomainBed.git cd DomainBed pip install -r requirements.txt这里要提醒一句requirements.txt 里的包比较基础没装 pandas、numpy、tqdm 的话一并装上。如果网络环境特殊导致安装慢用国内镜像源替换默认源效率会高很多。4.2 准备数据ColoredMNIST 与 DomainNet 的下载与生成DomainBed 对每个数据集的加载逻辑封装在domainbed/datasets.py里调用时指定--dataset名称即可框架会自动判断数据目录是否存在不存在就尝试下载或生成。我最常用的是 ColoredMNIST。它不需要预先下载任何外部数据框架会在运行时从 MNIST 原始数据中动态生成带颜色的版本。MVIST 原始数据可以从 torchvision 自动下载如果下载超时可以手动下载到~/.torch/datasets/MNIST/目录。DomainNet 的准备工作就比较费时了。整个数据集压缩包大约十几个 GB解压后占用大量磁盘空间。官方下载地址在 datasets 的 README 里需要把 6 个域的 tar 包全部下载并解压到同一个目录。下载慢是最常见的问题建议使用带断点续传的下载工具。解压完成后目录结构是每个域一个子文件夹里面按 345 个类别分子目录。运行训练命令时需要把数据集的父目录通过环境变量或命令行参数传给脚本。我在第一次跑的时候忘了指定数据路径框架直接去默认目录找结果报了一堆 FileNotFoundError检查了半天才发现是路径没配对。命令行里的--data_dir参数一定要看清楚。4.3 训练一个 ERM 基线以 ColoredMNIST 为例跑一个 ERM 基线只需要一条命令python -m domainbed.scripts.train \ --dataset ColoredMNIST \ --algorithm ERM \ --test_env 2 \ --data_dir /path/to/data \ --output_dir /path/to/output \ --trial_seed 0 \ --steps 5000这里--test_env 2表示把第三个环境编号从 0 开始作为测试域。ColoredMNIST 默认产生三个环境前两个是训练环境第三个是测试环境如果不传--test_env框架默认用最后一个环境。--steps 5000是训练步数ColoredMNIST 是轻量数据集5000 步足够到尾部观察 loss 基本收敛。训练过程中终端会打印每一轮的 loss 和准确率训练结束后输出目录里会生成results.csv记录该 trial 的最终测试精度和超参数配置。如果你跑多个--trial_seed最终可以用官方提供的domainbed/scripts/collect_results.py汇总所有 trial 结果并计算平均值。4.4 自定义数据集与算法的接入点DomainBed 不只是一个跑现成实验的盒子它还预留了清晰的扩展点。自定义数据集需要实现一个 PyTorch Dataset 类在__getitem__里返回图像和标签并在datasets.py里注册该数据集名称、域划分方式和输入通道数。之后就能用--dataset 自定义名称训练了。自定义算法稍微复杂一些。每个算法都继承自Algorithm基类核心要重写的方法有update和predict。update接收一个 batch 数据返回损失并更新模型参数predict接收图像张量返回分类 logits。实现了之后再在algorithms.py里注册算法名称即可与内置算法放一起对比。DomainBed 这种插件式设计让复现新论文变得很方便。我拿到一篇新 DG 论文时通常先把它的方法写成 DomainBed 里的一个 Algorithm 类再跑对比实验这样能最大程度避免因为训练细节不同带来的偏差。5. 深入一点ColoredMNIST 背后的构造逻辑与它验证了什么5.1 颜色-标签相关性的数学构造ColoredMNIST 看起来只是一个给 MNIST 上色的简单操作但它背后的设计非常精密。原始 MNIST 是 10 分类DomainBed 先把数字 0 到 4 映射为类别 0数字 5 到 9 映射为类别 1并分别打上代表正负性的标签。接着构造颜色偏置对每个样本以一定概率给它加上红色或蓝色的通道偏置。具体来说训练环境里颜色与标签的相关性是 80% 和 90%测试环境里下降到 10%。当相关性是 80% 时意味着属于类别 0 的样本有 80% 被染成红色系20% 被染成蓝色系类别 1 则相反。测试集相关性只有 10%也就是红的对应类别 0这条规律被彻底逆转模型如果学会了颜色捷径在测试集上准确率会跌到接近随机水平。为了避免模型通过边缘噪声等其他线索兜底图像上还叠加了随机高斯噪声进一步排除非形状特征。注意这里说的相关性不是控制颜色强度的百分百覆盖而是概率相关的数据生成过程。这种设计保证了训练域和目标域之间只改变颜色和标签的相关性而其他条件保持不变因此任何性能差异都能归因到模型是否依赖于颜色捷径。5.2 为什么好的 DG 算法必须忽略颜色在 ColoredMNIST 上一个标准的 ERM 模型训练时损失函数会非常快地下降训练准确率能到 90% 以上。但如果测试域颜色相关性反转测试准确率可能只有 30% 上下——比随机猜还差因为它不仅没学到有用信号还学会了错误信号。好的 DG 算法需要在训练时就能识别出颜色是不可靠特征并主动降低对它的依赖。IRM不变风险最小化这类算法的核心思想是找到在多个训练环境上同时最优的特征表示。如果某个特征是虚假相关的那么它在不同环境上的最优分类器就会不一致IRM 会通过约束条件迫使模型放弃这种特征。在 ColoredMNIST 上一个成功的算法在测试域上的准确率应该明显高于依赖颜色的 ERM。从我实测的经验看ColoredMNIST 对很多算法来说是一道照妖镜。有些算法在 PACS 上看起来效果不错跑到 ColoredMNIST 上就露馅说明它其实还在学捷径。这也是为什么论文里如果想证明算法学到了不变特征通常都会放一张 ColoredMNIST 的结果。5.3 从 ColoredMNIST 到真实世界的启示有人可能会觉得 ColoredMNIST 太玩具和真实场景差距太大。但我越来越认同它的价值分布偏移的机制是高度可解释的这让研究者能把模型在做什么看得一清二楚。真实场景里很多失败案例本质就是模型依赖了某种虚假相关性。比如水下机器人识别鱼的时候海水颜色偏蓝模型可能把蓝色当成鱼的线索换到不同水质的水域就失效。医疗影像中不同医院的扫描设备在图像对比度、色调上有系统性差异模型可能靠亮度风格分类而不是病灶特征跨医院部署准确率骤降。因为真实数据里你很难精确控制哪个特征是虚假的所以先用 ColoredMNIST 把机制验证清楚再迁移到真实数据的推理是性价比最高的研究路径。6. 案例视角在 DomainNet 上做对比实验的完整过程6.1 选定目标域与基线我在实际工作中常用 DomainNet 做最终对比这里分享一个完整案例。假设我要评估两个域泛化算法和一个基线在 6 个域中选一个作为目标域同时要顾及训练成本和结果可解释性。选择sketch作为目标域是因为它风格鲜明线稿特征和真实图像的差异很大最能看出算法有没有真正学到跨域不变性。训练命令如下python -m domainbed.scripts.train \ --dataset DomainNet \ --algorithm ERM \ --test_env 5 \ --data_dir /disk/data/domainnet \ --output_dir /disk/experiments/domainnet \ --trial_seed 0 --seed 0 \ --steps 5000 \ --batch_size 32在 DomainNet 上训练比 ColoredMNIST 耗时得多5000 步在单张 V100 上大约要跑大半天。如果机器资源紧张可以先用 DomainNet126 子集跑通流程再上全量数据集。6.2 观察结果哪些域最难把 6 个域都作为目标域跑一遍之后结果规律性很强quickdraw 通常是最难的目标域其次是 infograph 和 sketch。原因在于这三个域的视觉分布与 real 域差距最大quickdraw 是涂鸦线条画infograph 是带文本的信息图模型在 real 和 clipart 上学到的特征很难迁移过去。相比之下painting 和 clipart 稍稍好一些因为它们虽然风格不同但物体轮廓和形状信息保留得更加完整。还有一个常见现象是 DomainNet 上的绝对精度并不高。即便是最强的方法在 quickdraw 目标域上准确率也往往只有 40% 上下能到 50% 以上就是相当不错的成绩了。这不代表模型没用345 类的复杂度本身就是一场压力测试类间相似性高、类别间样本数量极不平衡都在拖累最终分数。6.3 常见问题与坑在 DomainNet 上跑实验比在 PACS 上容易踩坑我列几个自己踩过的第一个坑是显存不足。DomainNet 类别多、图像尺寸大batch size 32 在某些 11GB 卡上已经接近极限。遇到 Out of Memory 时不要急着换小模型先尝试减小 batch size 并用梯度累积补偿。DomainBed 本身没有内置梯度累积需要手动在训练循环里实现或者直接调到 16 并降低总步数也能得到一个可参考的对比结果。第二个坑是下载的 DomainNet 数据没有正确组织目录结构。DomainBed 要求每个域一个目录目录下按类别分子目录类别目录里是图片。如果直接把所有图片平铺在一个文件夹里加载时会报错。下载完解压之后务必先检查目录层级。第三个坑是训练步数不一致导致的对比失真。DomainBed 默认按数据集设定训练步数但如果你自己调整了--steps一定要保证所有算法都用同样的步数跑。步数不同收敛程度不同精度差异可能被误读为算法能力差异。第四个坑是大模型的随机波动。DomainNet 类别多相同的训练配置在不同随机种子下的结果可能相差 2 到 3 个百分点。单一 trial 的结果根本不足以做判断至少跑三个 seed 取平均。我在复现某算法时单 seed 结果比 ERM 高 4 个点扩展到五个 seed 后差距缩小到 1 个点最后发现那 1 个点也主要是因为随机种子选择带来的波动多跑两组就没了。我在实际使用 DomainBed 时最深的一点体会是它强迫你尊重评估的公平性。早期我会忍不住在源域验证集上多调几次超参数直到验证效果很好再测目标域这种做法在普通实验流程里看起来没什么问题但本质上已经引入了一定程度的过拟合。DomainBed 通过固定的搜索空间和固定的验证集切分把这种自由度降到了最低。对于刚接触域泛化的研究者我的建议是先用 ColoredMNIST 把框架跑通再用 PACS 感受一下风格差异带来的影响最后才碰 DomainNet——如果一上来就啃 DomainNet光是下载数据和处理训练时间就够耗掉大半耐心。这套工具并不是完美的它也有一些为了公平而牺牲灵活性的地方但在域泛化评测这件事上它是一个比自己搭评测流程可靠得多的选择。