实验设计原则:如何设计一个可重复、可分析的实验?

做AI模型,最怕什么?

我最怕的是:跑了一周的实验,结果复现不出来。

你想想看,同样的数据,同样的代码,换台机器结果就变了。或者更糟——你根本记不清当时用了什么参数。这种坑,我踩过不止一次。

所以今天聊的实验设计,说白了就是给你的实验上个「保险」。让结果可重复、可分析,而不是靠运气。

一、控制变量法:别让无关因素干扰你

控制变量法,听起来像初中物理课的内容。但在AI实验里,它是最容易被忽略的。

我见过太多人,调模型时同时改了学习率、batch size、网络层数。结果模型变好了,你根本不知道是哪个改动起了作用。

核心原则:一次只变一个变量。

其他所有条件保持一致,包括随机种子、数据切分方式、硬件环境。

举个例子。你想测试是否要加Dropout层。那么实验组和对照组应该这样设计:

变量 实验组 对照组
网络结构 加Dropout (p=0.5) 不加Dropout
学习率 0.001 0.001
batch size 32 32
随机种子 42 42
数据切分 相同 相同

嗯,这里要注意:连数据加载的顺序都要保持一致。我遇到过因为DataLoader的shuffle参数没固定,导致两次实验的验证集分布不同,结果完全没法比。

我的习惯:每次实验前,先写一个配置文件(YAML或JSON),把超参数、数据路径、随机种子全部固定下来。实验结束后,这个配置文件就是你的「实验身份证」。

二、随机化:消除隐藏的偏差

控制变量法解决了「变量太多」的问题。但还有一个问题:数据本身可能有顺序偏差。

比如你的数据是按时间排序的。前1000条是晴天数据,后1000条是雨天数据。如果不做随机化,模型可能只学会了「晴天模式」。

随机化要怎么做?我个人建议分三层:

  1. 数据层面的随机化:训练集、验证集、测试集的划分要随机。不要按文件名的字母顺序切分。
  2. 训练过程中的随机化:每个epoch打乱数据顺序。这能防止模型记住样本顺序。
  3. 实验顺序的随机化:如果你要比较多个超参数组合,不要按顺序跑。比如先跑学习率0.001,再跑0.01。万一服务器在跑第二个实验时负载变高了呢?随机化实验顺序,可以抵消这种时间上的干扰。

我曾经踩过的坑:有一次做对比实验,A模型和B模型交替跑。结果A模型总是在白天跑,B模型总是在晚上跑。晚上服务器空闲,训练速度更快,但B模型用了更少的epoch就收敛了。最后发现是实验调度脚本的问题。从那以后,我坚持用随机顺序跑实验。

三、样本量估算:别做无意义的实验

样本量太小,结果不可靠。样本量太大,浪费计算资源。

那到底需要多少样本?

我一般用这个经验公式:

# 简单估算样本量
# 适用于分类任务
import math

def estimate_sample_size(effect_size=0.5, power=0.8, alpha=0.05):
    """
    effect_size: 预期效果大小(Cohen's d)
    power: 统计功效(通常0.8)
    alpha: 显著性水平(通常0.05)
    """
    z_alpha = 1.96  # 对应alpha=0.05
    z_beta = 0.84   # 对应power=0.8
    
    n = 2 * ((z_alpha + z_beta) / effect_size) ** 2
    return math.ceil(n)

# 假设你预期模型A比模型B准确率高5%
# 换算成effect_size大约0.3
n = estimate_sample_size(effect_size=0.3)
print(f"每组至少需要 {n} 个样本")

当然,这只是粗略估算。实际项目中,我还会考虑:

  • 类别平衡:如果正负样本比例是1:9,那需要的总样本量会大很多
  • 指标波动性:如果准确率本身波动大(比如从70%到90%乱跳),需要更多样本才能看到稳定趋势
  • 计算资源:有时候不是「需要多少」,而是「能跑多少」。这时候要权衡精度和成本

一个实用的建议:先跑一个小规模的预实验(比如10%的数据),看看指标的方差有多大。然后根据方差反推需要的样本量。这比拍脑袋决定靠谱得多。

四、知识体系总览

下面这张图,概括了实验设计的核心逻辑。你可以把它当作实验前的检查清单。

实验设计三大原则 控制变量法 一次只变一个变量 随机化 消除顺序与时间偏差 样本量估算 确保统计显著性 具体做法 • 固定随机种子 • 使用配置文件 • 保持数据切分一致 • 记录所有超参数 具体做法 • 数据划分随机化 • 训练顺序随机化 • 实验顺序随机化 • 交叉验证 具体做法 • 预实验估算方差 • 考虑类别平衡 • 计算统计功效 • 权衡精度与成本 三者缺一不可,共同保证实验的可重复性与可分析性

五、避坑指南:我踩过的那些坑

最后分享几个实战中容易忽略的细节:

  • 随机种子要全局设置:不光要设Python的random.seed,还要设numpy和pytorch/tensorflow的种子。我见过只设了Python种子,但numpy还是随机的——结果两次实验数据划分不同。
  • GPU的确定性计算:GPU的并行计算本身有随机性。需要设置torch.backends.cudnn.deterministic = True。但注意,这会降低训练速度。
  • 数据预处理的一致性:训练时和推理时的预处理要完全一致。包括归一化的均值、标准差,甚至图像resize的插值方式。
  • 记录实验环境:不光要记录代码版本,还要记录Python版本、CUDA版本、GPU型号。我遇到过因为CUDA版本不同,导致结果有微小差异的情况。

我的一个小技巧:每次实验结束后,自动生成一个实验报告。包含所有超参数、关键指标、以及环境信息。这样三个月后回头看,还能知道当时发生了什么。

实验设计这件事,说难不难,说简单也不简单。说白了就是养成好习惯。刚开始可能觉得麻烦,但当你需要复现一个三个月前的实验时,你会感谢当初认真做实验设计的自己。


公众号:蓝海资料掘金营,微信deep3321