橘智橘智
FakeOrange
预计阅读时间:2分钟55秒

关于使用Pytorch构建深度学习模型对比Keras需要注意的问题

近期发现在使用Keras进行同样模型测试时,pytorch的收敛效果及性能不如keras

0
0


前言



近期在Kaggle比赛中发现,模型训练中Loss不稳定。看了相关内容之后发现可能是Pytorch的问题。在一些人的测试中Keras在同样实行统一模型时的效果要比Pytorch更加好。


我本人比较偏向Pytorch因为容易客制化一些模型参数。其实问题就在这里,Pytorch带来的高度客制化的操作方式,会让你在某些环节忽略。


当一个PyTorch模型的性能比Keras模型差,即使两者的实现看似相同时,可能有多个因素导致这种差异。下面我们从权重初始化、优化器、梯度裁剪等方面的详细分析:



1. 权重初始化



权重初始化的方式在某些情况下会导致收敛速度及效果的差异。


  • 默认差异
    • PyTorch层通常使用 Kaiming UniformXavier Uniform,具体取决于激活函数。(有些新手可能会忽略这个部分)
    • Keras默认使用 Glorot Uniform (Xavier Uniform)。更加细致的初始化方式。


  • 解决方案
    • 在两种实现中显式设置相同的初始化方式。


# PyTorch 示例
def _initialize_weights(self):
    for m in self.modules():
        if isinstance(m, nn.Conv3d) or isinstance(m, nn.ConvTranspose3d):
            # For ReLU activation (He initialization)
            nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='leaky_relu')
        elif isinstance(m, nn.Linear):
            # Xavier initialization for fully connected layers
            nn.init.xavier_uniform_(m.weight)
        elif isinstance(m, nn.BatchNorm3d):
            # Initialize BatchNorm weights
            nn.init.constant_(m.weight, 1)
            nn.init.constant_(m.bias, 0)
# Keras 示例
keras.layers.Dense(units, kernel_initializer='glorot_uniform')



2. 优化器和超参数



以 AdamW 举例,学习率这个参数属于大家都会按照自己的需求去调整的,但是对于其他的一些默认值Keras 与 Pytorch是有所不同的。


  • 默认优化器行为
    • Pytorch Weight Decaying 的默认值为0.01,eps 默认为1e-8
    • Keras Weight Decaying 的默认值为0.004,eps 默认为1e-7
    • 在以上的Weight Decaying之下,他们的默认学习率相同。


以上的微小差异,在训练过程中可能产生累加影响。



3. 梯度裁剪



  • 裁剪差异
    • Keras通常通过优化器参数无缝集成梯度裁剪,而PyTorch需要通过hooks或torch.nn.utils.clip_grad_norm_/clip_grad_value_显式实现。
    • 在PyTorch中缺乏梯度裁剪可能导致梯度爆炸。


  • 解决方案
    • 确保在两种实现中以相同方式裁剪梯度。


# PyTorch 梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# Keras 梯度裁剪
keras.optimizers.Adam(clipnorm=1.0)



完整运行


from torch.cuda.amp import autocast, GradScaler

# Initialize optimizer and scaler
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
scaler = GradScaler()

for epoch in range(num_epochs):
    for inputs, labels in dataloader:
        optimizer.zero_grad()

        # Forward pass with autocast
        with autocast():
            outputs = model(inputs)
            loss = criterion(outputs, labels)

        # Scale loss and backpropagate
        scaler.scale(loss).backward()

        # Gradient clipping (optional)
        scaler.unscale_(optimizer)
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

        # Optimizer step
        scaler.step(optimizer)
        scaler.update()

        # Monitor loss
        print(f"Loss: {loss.item()}")



总结



PyTorch和Keras模型性能差异通常源于默认设置或手动实现步骤中的细微差异。通过系统性地对齐权重初始化、优化器、梯度裁剪、数据预处理和训练循环,可以缩小差距并在两种框架中实现一致的结果。


评论