【问题标题】:PyTorch - Creating Federated CIFAR-10 DatasetPyTorch - 创建联合 CIFAR-10 数据集
【发布时间】:2021-02-01 00:31:42
【问题描述】:

我正在 CIFAR-10 数据集上训练一个神经网络(不管是哪一个)。我正在使用联邦学习:

  • 我有 10 个模型,每个模型都可以访问自己的数据集部分。在每个时间步,每个模型使用自己的数据做一步,然后全局模型是模型的平均值(这个版本是基于this,但我尝试了很多选项):
def server_aggregate(server_model, client_models):
    global_dict = server_model.state_dict()
    for k in global_dict.keys():
        global_dict[k] = torch.stack([client_models[i].state_dict()[k].float() for i in range(len(client_models))], 0).mean(0)
    server_model.load_state_dict(global_dict)
    for model in client_models:
        model.load_state_dict(server_model.state_dict())
  • 具体来说,每台机器只能访问与单个类对应的数据。 IE。机器0 仅具有与0 类对应的样本等。我这样做的方式如下:
def split_into_classes(full_ds, batch_size, num_classes=10):
  class2indices = [[] for _ in range(num_classes)]
  for i, y in enumerate(full_ds.targets):
    class2indices[y].append(i)

  datasets = [torch.utils.data.Subset(full_ds, indices) for indices in class2indices]
  return [DataLoader(ds, batch_size=batch_size, shuffle=True) for ds in datasets]

问题。在训练期间,我可以看到我的联合训练损失减少了。但是,我从来没有看到我的测试损失/准确性提高(acc 总是在 10% 左右)。 此外,当我检查train/test datasets 的准确性时:

  • 对于联合数据集,准确性有所提高。
  • 对于测试数据集,准确度没有提高。
  • (最令人惊讶)对于训练数据集,准确度没有提高。请注意,此数据集本质上与联邦数据集相同,但未拆分为类。检查码是following
def epoch_summary(model, fed_loaders, true_train_loader, test_loader, frac):
  with torch.no_grad():
    train_len = 0
    train_loss, train_acc = 0, 0
    for train_loader in fed_loaders:
      cur_loss, cur_acc, cur_len = true_results(model, train_loader, frac)
      train_loss += cur_len * cur_loss
      train_acc += cur_len * cur_acc
      train_len += cur_len

    train_loss /= train_len
    train_acc /= train_len

    true_train_loss, true_train_acc, true_train_len = true_results(model, true_train_loader, frac)
    test_loss, test_acc, test_len = true_results(model, test_loader, frac)

  print("TrainLoss: {:.4f} TrainAcc: {:.2f} TrueLoss: {:.4f} TrueAcc: {:.2f} TestLoss: {:.4f} TestAcc: {:.2f}".format(
        train_loss, train_acc, true_train_loss, true_train_acc, test_loss, test_acc
        ), flush=True)

完整代码可以在here找到。似乎无关紧要的事情:

  • 型号。对于 Resnet 模型和其他一些模型,我遇到了同样的问题。
  • 我如何聚合模型。我试过用state_dict或者直接操作model.parameters(),没有效果。
  • 我如何学习模型。我试过用optim.SGD或者直接更新param.data -= learning_rate * param.grad,没有效果。
  • 计算图。我尝试将.detach().clone()with torch.no_grad() 添加到所有可能的位置,但没有效果。

所以我怀疑问题出在联合数据本身(特别是考虑到奇怪的准确性结果)。可能是什么问题?

【问题讨论】:

  • “在训练过程中,我可以看到我的训练损失减少了。但是,我从来没有看到我的测试损失/准确性提高”是整个火车上的联邦模型/测试数据集?
  • 我不确定我是否理解这个问题,但是可以。请注意,我根据完整的训练数据估计评估模型(因此它应该是完美的,但事实并非如此)。
  • @Ivan,实验表明问题出在批量标准化(当我使用密集模型或用身份层替换 BN 层时,它可以工作)。看起来你不应该这样平均。您知道在模型平均期间处理它们的正确方法是什么吗?
  • "machine 0 只有与 class 0 对应的样本" 我不明白你将如何为 1-class 训练模型如果机器0 只有0 类的数据点,则分类任务。这对我来说没有意义。
  • 我认为Local SGD Converges Fast and Communicates Little 涵盖了我提到的两点。

标签: python neural-network pytorch


【解决方案1】:

CIFAR-10 上的 10% 基本上是随机的 - 您的模型随机输出标签并获得 10%。

我认为问题在于您的“联合训练”策略:当您的子模型看到的只是一个标签时,您不能指望他们学习任何有意义的东西。这就是为什么训练数据is shuffled.
想一想:如果您的每个子模型都学习到除了最后一个分类层的bias 向量之外的所有权重为零,该向量在与该子模型看到的类对应的条目中具有1 - 每个子模型的训练子模型是完美的(它对于它看到的所有训练样本都是正确的),但是平均模型是没有意义的。

【讨论】:

  • 只要步长足够小,无论数据分布如何,联邦策略都能保证收敛(它会找到训练数据上梯度为 0 的点)。在我的第二个链接中,我执行以下操作:在每个步骤中,我为每台机器选择一个批次,对它们进行批次训练,然后对模型进行平均。这完全等同于选择一个 10 倍大小的批次,其中每个类别占批次的 10%。效果是一样的。
  • [考虑到“每台机器只能访问[...]单个类”]。 “这完全等同于选择一个批次”这不是真的。分别训练这些模型然后进行平均绝对不同于使用单个模型对整个类集进行训练。你为什么会这样假设?
  • @Ivan。 (请标记我,以便我可以看到回复)让x 为当前模型参数(每台机器具有相同的参数)。对于每台机器,更新为x_i <- x - lr * grad_i(x)。请注意,我在每一步之后对模型进行平均,因此新的全局模型是avg(x_i) = x - lr * avg(grad_i(x))。每个grad_iith 模型批次中样本损失的平均梯度。由于所有批次大小都相等,avg(grad_i) 是参与该步骤的所有样本的平均损失梯度。换句话说,它等于[查看我的第一条评论]
  • @Shai,实验表明问题出在批量标准化(当我使用密集模型或用身份层替换 BN 层时,它可以工作)。看起来你不应该这样平均。您知道在模型平均期间处理它们的正确方法是什么吗?
  • @Dmitry 这一切似乎都源于您拆分数据的非随机方式。你可以尝试随机分割吗?你为什么坚持这种分裂?
猜你喜欢
  • 2017-05-15
  • 2020-10-14
  • 2016-05-04
  • 2021-05-01
  • 2018-06-22
  • 2015-11-13
  • 2022-12-04
  • 2017-12-19
  • 2019-03-27
相关资源
最近更新 更多