当前位置:   article > 正文

mnist数据集训练_Part 06 在MNIST数据集上执行联邦学习训练CNN模型

mnist数据集上训练cnn网路 联邦学习

Time: 2019-11-19

教程作者:Théo Ryffel -

GitHub: [@LaRiffle](LaRiffle - Overview)

1. 上下文

联邦学习是非常令人激动且日益火热的机器学习技术,只在构建一种这样的系统:能够在分布式数据上学习。

想法是让数据仍然保持在数据生产者手里(也叫作_worker_),此想法能够保护隐私和所有权,只是将模型在结点之间进行共享。

一个能够立马用上的应用是,在手机上书写文本时帮你预测下一个单词:而你并不想这些数据发送到中心化服务器上用于训练,比如你的短信

联邦学习的兴起和数据隐私意识的觉醒有很大的关系,比如GDPR法案,强制数据保护。

谷歌和苹果已经开始大量投入资金研发这种技术,但是他们还没有公开自己的工具。

OpenMined相信任何人想要开发、部署机器学习模型应当能够花费少量的力气就能完成隐私保护。

我们构建了一个工具,一行代码就能加密数据,参考[使用SPDZ训练CNN](Training a CNN using SPDZ)。

现在我们发布的联邦学习框架,综合了PyTorch框架用于提供一个易用的接口来完成构建安全和可扩展的模型。

本章,我们将直接使用经典的案例,[使用PyTorch在MNIST上训练CNN模型](https://github.com/pytorch/examples/blob/master/mnist/main.py),然后使用PySyft框架将其改造为联邦学习方法。

本章将会深入讲解每一行代码改造背后的原理。

这个材料也可以参考[博客: 10行代码改造模型为联邦学习]((Federated Learning in 10 lines))。

开始吧

2. 实验

1) 标准的PyTorch/TorchVision的包导入

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. import torch.optim as optim
  5. from torchvision import datasets, transforms

然后是PySyft的准备:

  1. import syft as sy # <-- NEW: import the Pysyft library
  2. hook = sy.TorchHook(torch) # <-- NEW: hook PyTorch ie add extra functionalities to support Federated Learning
  3. bob = sy.VirtualWorker(hook, id="bob") # <-- NEW: define remote worker bob
  4. alice = sy.VirtualWorker(hook, id="alice") # <-- NEW: and alice

2) 定义学习任务的设定(超参数):

  1. class Arguments():
  2. def __init__(self):
  3. self.batch_size = 64
  4. self.test_batch_size = 1000
  5. self.epochs = 10
  6. self.lr = 0.01
  7. self.momentum = 0.5
  8. self.no_cuda = False
  9. self.seed = 1
  10. self.log_interval = 30
  11. self.save_model = False
  12. args = Arguments()
  13. use_cuda = not args.no_cuda and torch.cuda.is_available()
  14. torch.manual_seed(args.seed)
  15. device = torch.device("cuda" if use_cuda else "cpu")
  16. kwargs = {'num_workers': 1, 'pin_memory': True} if use_cuda else {}

这个写法可以作为使用PyTorch框架开发的标准流程。

用类来组织在PyTorch中的代码。

3) 数据加载并发送到节点

节点应该是worker更精准的翻译。

我们首先加载数据然后转换训练数据集为联邦学习用的数据集,并将数据集分割分发到多个节点。

PS. 数据集还是我们本机先有的,处理好发送到的其他节点,如何在其他节点上已经有了数据,远程预处理呢?这些目前还没看到方法。

使用方法.federate,联邦数据集现在就交给了联邦型的DataLoader,测试数据集保持不变。

改造训练数据集:

  1. federated_train_loader = sy.FederatedDataLoader(
  2. datasets.MNIST('../data', train=True, download=True, transform=transforms.Compose([
  3. transforms.ToTensor(),
  4. transforms.Normalize((0.1307,),(0.3081,)) # 这是ImageNet数据集的方差和均值
  5. ])).federate((bob, alice)), # 将数据集分发到所有的指定节点,现在是联邦数据集了
  6. batch_size=args.batch_size, shuffle=True, **kwargs)

数据是每个人拿到一样还是如何分割的呢?

无需改造训练数据集:

  1. test_loader = torch.utils.data.DataLoader(
  2. datasets.MNIST('../data', train=False, transform=transforms.Compose([
  3. transforms.ToTensor(),
  4. transforms.Normalize((0.1307,), (0.3081,))
  5. ])),
  6. batch_size=args.test_batch_size, shuffle=True, **kwargs)

4) CNN模型准备

现在我们使用CNN模型,和官方样例相同。

MNIST数据集的单张图片大小是28x28x1,所以按照下面的模型定义,单张图片最后输出的大小是4x4x50

28 x 28 ->(conv1) 24 x 24 ->(max_pool2d) 12 x 12 ->(conv2) 8 x 8 ->(max_pool2d) 4x4 是可以直接推算出来的。

  1. class Net(nn.Module):
  2. def __init__(self):
  3. super(Net, self).__init__()
  4. self.conv1 = nn.Conv2d(1, 20, 5, 1)
  5. self.conv2 = nn.Conv2d(20, 50, 5, 1)
  6. self.fc1 = nn.Linear(4*4*50, 500)
  7. self.fc2 = nn.Linear(500, 10)
  8. def forward(self, x):
  9. x = F.relu(self.conv1(x))
  10. x = F.max_pool2d(x, 2, 2)
  11. x = F.relu(self.conv2(x))
  12. x = F.max_pool2d(x, 2, 2)
  13. x = x.view(-1, 4*4*50)
  14. x = F.relu(self.fc1(x))
  15. x = self.fc2(x)
  16. return F.log_softmax(x, dim=1)
  17. net = Net()
  18. print(net)
  19. '''
  20. Net(
  21. (conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
  22. (conv2): Conv2d(20, 50, kernel_size=(5, 5), stride=(1, 1))
  23. (fc1): Linear(in_features=800, out_features=500, bias=True)
  24. (fc2): Linear(in_features=500, out_features=10, bias=True)
  25. )
  26. '''

5) 定义训练和测试函数

训练过程

针对训练过程而言,因为训练数据是分布在alicebob对应的节点上,每一个batch,我们都需要将模型发送到正确的位置。

然后,在远程执行这些操作,语法和在本地训练PyTorch模型一致。

结束后,你将更新好的模型和损失函数值拿回来,然后想办法更新模型。

  1. def train(args, model, device, federated_train_loader, optimizer, epoch):
  2. model.train() # 设置模型为训练模式
  3. for batch_index, (data, target) in enumerate(federated_train_loader):
  4. model.send(data.location)
  5. data, target = data.to(device), target.to(device) # 如果有GPU搬到GPU计算
  6. optimizer.zero_grad()
  7. output = model(data)
  8. loss = F.nll_loss(output, target)
  9. loss.backward() # 计算梯度
  10. optimizer.step() # 更新参数
  11. model.get() # 取回模型
  12. if batch_index % args.log_interval == 0:
  13. loss = loss.get() # 每隔一段时间取回loss看看
  14. print('Train Epoch: {} [{}/{} ({:.0f}%)]tLoss: {:.6f}'.format(
  15. epoch, batch_idx * args.batch_size, len(federated_train_loader) * args.batch_size,
  16. 100. * batch_idx / len(federated_train_loader), loss.item()))
  17. ```

测试函数不变:

  1. def test(args, model, device, test_loader):
  2. model.eval() # 设置模型为评估模式
  3. test_loss = 0
  4. correct = 0
  5. with torch.no_grad():
  6. for data, target in test_loader:
  7. data, target = data.to(device), target.to(device)
  8. output = model(data)
  9. test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
  10. pred = output.argmax(1, keepdim=True) # get the index of the max log-probability
  11. correct += pred.eq(target.view_as(pred)).sum().item()
  12. test_loss /= len(test_loader.dataset)
  13. print('nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)n'.format(
  14. test_loss, correct, len(test_loader.dataset),
  15. 100. * correct / len(test_loader.dataset)))

6) 训练流程跑起来

model.train(), model.eval()只在特定的模型上有效果。不要理解为定义好模型就能训练了~~

  1. %%time
  2. model = Net().to(device)
  3. optimizer = optim.SGD(model.parameters(), lr=args.lr) # TODO momentum is not supported at the moment
  4. for epoch in range(1, args.epochs + 1):
  5. train(args, model, device, federated_train_loader, optimizer, epoch)
  6. test(args, model, device, test_loader)
  7. if (args.save_model):
  8. torch.save(model.state_dict(), "mnist_cnn.pt")

训练log(节选):

  1. Train Epoch: 4 [59520/60032 (99%)] Loss: 0.057204
  2. Test set: Average loss: 0.0534, Accuracy: 9828/10000 (98%)
  3. Train Epoch: 5 [0/60032 (0%)] Loss: 0.161142
  4. Train Epoch: 5 [1920/60032 (3%)] Loss: 0.031823

可以看到在测试集上的平均损失是:0.0534,准确率为98%(训练4个epoch)。

非联邦学习的训练方法中,4个epoch的训练效果如下:

  1. Train Epoch: 4 [56320/60000 (94%)] Loss: 0.029797
  2. Train Epoch: 4 [56960/60000 (95%)] Loss: 0.016238
  3. Train Epoch: 4 [57600/60000 (96%)] Loss: 0.090854
  4. Train Epoch: 4 [58240/60000 (97%)] Loss: 0.088320
  5. Train Epoch: 4 [58880/60000 (98%)] Loss: 0.097426
  6. Train Epoch: 4 [59520/60000 (99%)] Loss: 0.006990
  7. Test set: Average loss: 0.0348, Accuracy: 9897/10000 (99%)

3. 最后一件事情

相比较于普通的PyTorch训练方式,联邦学习训练方式耗时有多长呢?

计算时间上,是普通的训练方式的1.9倍,不过和后面我们要添加的特性来说这是不值一提的时间消耗。

总结

我们大概修改了10行左右的代码就完成了从官方PyTorch在MNIST数据集上的训练样例到真实的联邦学习设定的改造。

是的,还有很多可以改进的地方。我们可以让计算并行操作(毕竟是分布在多个节点上),以及执行联邦均值,每隔n个batch更新中心服务器上的模型以便减少通信消耗。

此外,还有许多新特性可以添加以使得联邦学习能够用在产品环境中,等到特性公布后我们会即时更新相关教程。

现在你应当能自己实现联邦学习模型了。

Peace & Love.

Enjoy.

END at 2019-11-19 9:24 pm

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号